elit 3.6.7 → 3.6.9
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +20 -1
- package/dist/cli.cjs +2777 -321
- package/dist/cli.mjs +2764 -308
- package/dist/config.d.ts +6 -6
- package/dist/{contracts-BeW9k0yZ.d.ts → contracts-_0p1-15U.d.ts} +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/dev-build.d.ts +3 -3
- package/dist/http.cjs +160 -41
- package/dist/http.d.ts +5 -11
- package/dist/http.js +160 -41
- package/dist/http.mjs +160 -41
- package/dist/https.cjs +194 -46
- package/dist/https.d.ts +3 -6
- package/dist/https.js +194 -46
- package/dist/https.mjs +194 -46
- package/dist/pm-node-shared-listener-bootstrap.cjs +75 -0
- package/dist/pm.cjs +2390 -160
- package/dist/pm.d.ts +83 -8
- package/dist/pm.js +2388 -188
- package/dist/pm.mjs +2380 -165
- package/dist/preview-build.d.ts +3 -3
- package/dist/server.cjs +417 -168
- package/dist/server.d.ts +4 -4
- package/dist/server.js +453 -179
- package/dist/server.mjs +417 -168
- package/dist/smtp-server.js +37 -12
- package/dist/test-reporter.d.ts +1 -1
- package/dist/test-runtime.d.ts +1 -1
- package/dist/{types-tJn88E1N.d.ts → types-BayMVo_k.d.ts} +39 -3
- package/dist/{types-CIhpN1-K.d.ts → types-C70T-42Z.d.ts} +1 -1
- package/dist/{types-DAisuVr5.d.ts → types-DPOgoGs-.d.ts} +7 -1
- package/dist/{state-DvEkDehk.d.ts → types-fiLday0L.d.ts} +96 -92
- package/dist/types.d.ts +4 -0
- package/dist/{websocket-XfyK23zD.d.ts → websocket-BLBEAnhp.d.ts} +1 -1
- package/dist/ws.d.ts +3 -3
- package/dist/wss.cjs +194 -46
- package/dist/wss.d.ts +3 -3
- package/dist/wss.js +194 -46
- package/dist/wss.mjs +194 -46
- package/package.json +11 -6
package/dist/pm.js
CHANGED
|
@@ -1677,7 +1677,7 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1677
1677
|
let latestResultPromise;
|
|
1678
1678
|
let provideLatestResult;
|
|
1679
1679
|
if (isContext)
|
|
1680
|
-
requestCallbacks["on-end"] = (id, request2) => new Promise((
|
|
1680
|
+
requestCallbacks["on-end"] = (id, request2) => new Promise((resolve7) => {
|
|
1681
1681
|
buildResponseToResult(request2, (err, result, onEndErrors, onEndWarnings) => {
|
|
1682
1682
|
const response = {
|
|
1683
1683
|
errors: onEndErrors,
|
|
@@ -1687,7 +1687,7 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1687
1687
|
latestResultPromise = void 0;
|
|
1688
1688
|
provideLatestResult = void 0;
|
|
1689
1689
|
sendResponse(id, response);
|
|
1690
|
-
|
|
1690
|
+
resolve7();
|
|
1691
1691
|
});
|
|
1692
1692
|
});
|
|
1693
1693
|
sendRequest(refs, request, (error, response) => {
|
|
@@ -1704,10 +1704,10 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1704
1704
|
let didDispose = false;
|
|
1705
1705
|
const result = {
|
|
1706
1706
|
rebuild: () => {
|
|
1707
|
-
if (!latestResultPromise) latestResultPromise = new Promise((
|
|
1707
|
+
if (!latestResultPromise) latestResultPromise = new Promise((resolve7, reject) => {
|
|
1708
1708
|
let settlePromise;
|
|
1709
1709
|
provideLatestResult = (err, result2) => {
|
|
1710
|
-
if (!settlePromise) settlePromise = () => err ? reject(err) :
|
|
1710
|
+
if (!settlePromise) settlePromise = () => err ? reject(err) : resolve7(result2);
|
|
1711
1711
|
};
|
|
1712
1712
|
const triggerAnotherBuild = () => {
|
|
1713
1713
|
const request2 = {
|
|
@@ -1728,7 +1728,7 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1728
1728
|
});
|
|
1729
1729
|
return latestResultPromise;
|
|
1730
1730
|
},
|
|
1731
|
-
watch: (options2 = {}) => new Promise((
|
|
1731
|
+
watch: (options2 = {}) => new Promise((resolve7, reject) => {
|
|
1732
1732
|
if (!streamIn.hasFS) throw new Error(`Cannot use the "watch" API in this environment`);
|
|
1733
1733
|
const keys = {};
|
|
1734
1734
|
const delay2 = getFlag(options2, keys, "delay", mustBeInteger);
|
|
@@ -1740,10 +1740,10 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1740
1740
|
if (delay2) request2.delay = delay2;
|
|
1741
1741
|
sendRequest(refs, request2, (error2) => {
|
|
1742
1742
|
if (error2) reject(new Error(error2));
|
|
1743
|
-
else
|
|
1743
|
+
else resolve7(void 0);
|
|
1744
1744
|
});
|
|
1745
1745
|
}),
|
|
1746
|
-
serve: (options2 = {}) => new Promise((
|
|
1746
|
+
serve: (options2 = {}) => new Promise((resolve7, reject) => {
|
|
1747
1747
|
if (!streamIn.hasFS) throw new Error(`Cannot use the "serve" API in this environment`);
|
|
1748
1748
|
const keys = {};
|
|
1749
1749
|
const port = getFlag(options2, keys, "port", mustBeValidPortNumber);
|
|
@@ -1781,28 +1781,28 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1781
1781
|
sendResponse(id, {});
|
|
1782
1782
|
};
|
|
1783
1783
|
}
|
|
1784
|
-
|
|
1784
|
+
resolve7(response2);
|
|
1785
1785
|
});
|
|
1786
1786
|
}),
|
|
1787
|
-
cancel: () => new Promise((
|
|
1788
|
-
if (didDispose) return
|
|
1787
|
+
cancel: () => new Promise((resolve7) => {
|
|
1788
|
+
if (didDispose) return resolve7();
|
|
1789
1789
|
const request2 = {
|
|
1790
1790
|
command: "cancel",
|
|
1791
1791
|
key: buildKey
|
|
1792
1792
|
};
|
|
1793
1793
|
sendRequest(refs, request2, () => {
|
|
1794
|
-
|
|
1794
|
+
resolve7();
|
|
1795
1795
|
});
|
|
1796
1796
|
}),
|
|
1797
|
-
dispose: () => new Promise((
|
|
1798
|
-
if (didDispose) return
|
|
1797
|
+
dispose: () => new Promise((resolve7) => {
|
|
1798
|
+
if (didDispose) return resolve7();
|
|
1799
1799
|
didDispose = true;
|
|
1800
1800
|
const request2 = {
|
|
1801
1801
|
command: "dispose",
|
|
1802
1802
|
key: buildKey
|
|
1803
1803
|
};
|
|
1804
1804
|
sendRequest(refs, request2, () => {
|
|
1805
|
-
|
|
1805
|
+
resolve7();
|
|
1806
1806
|
scheduleOnDisposeCallbacks();
|
|
1807
1807
|
refs.unref();
|
|
1808
1808
|
});
|
|
@@ -1841,7 +1841,7 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1841
1841
|
onLoad: []
|
|
1842
1842
|
};
|
|
1843
1843
|
i++;
|
|
1844
|
-
let
|
|
1844
|
+
let resolve7 = (path3, options = {}) => {
|
|
1845
1845
|
if (!isSetupDone) throw new Error('Cannot call "resolve" before plugin setup has completed');
|
|
1846
1846
|
if (typeof path3 !== "string") throw new Error(`The path to resolve must be a string`);
|
|
1847
1847
|
let keys2 = /* @__PURE__ */ Object.create(null);
|
|
@@ -1885,7 +1885,7 @@ is not a problem with esbuild. You need to fix your environment instead.
|
|
|
1885
1885
|
};
|
|
1886
1886
|
let promise = setup({
|
|
1887
1887
|
initialOptions,
|
|
1888
|
-
resolve:
|
|
1888
|
+
resolve: resolve7,
|
|
1889
1889
|
onStart(callback) {
|
|
1890
1890
|
let registeredText = `This error came from the "onStart" callback registered here:`;
|
|
1891
1891
|
let registeredNote = extractCallerV8(new Error(registeredText), streamIn, "onStart");
|
|
@@ -2772,46 +2772,46 @@ More information: The file containing the code for esbuild's JavaScript API (${_
|
|
|
2772
2772
|
}
|
|
2773
2773
|
};
|
|
2774
2774
|
longLivedService = {
|
|
2775
|
-
build: (options) => new Promise((
|
|
2775
|
+
build: (options) => new Promise((resolve7, reject) => {
|
|
2776
2776
|
service.buildOrContext({
|
|
2777
2777
|
callName: "build",
|
|
2778
2778
|
refs,
|
|
2779
2779
|
options,
|
|
2780
2780
|
isTTY: isTTY(),
|
|
2781
2781
|
defaultWD,
|
|
2782
|
-
callback: (err, res) => err ? reject(err) :
|
|
2782
|
+
callback: (err, res) => err ? reject(err) : resolve7(res)
|
|
2783
2783
|
});
|
|
2784
2784
|
}),
|
|
2785
|
-
context: (options) => new Promise((
|
|
2785
|
+
context: (options) => new Promise((resolve7, reject) => service.buildOrContext({
|
|
2786
2786
|
callName: "context",
|
|
2787
2787
|
refs,
|
|
2788
2788
|
options,
|
|
2789
2789
|
isTTY: isTTY(),
|
|
2790
2790
|
defaultWD,
|
|
2791
|
-
callback: (err, res) => err ? reject(err) :
|
|
2791
|
+
callback: (err, res) => err ? reject(err) : resolve7(res)
|
|
2792
2792
|
})),
|
|
2793
|
-
transform: (input, options) => new Promise((
|
|
2793
|
+
transform: (input, options) => new Promise((resolve7, reject) => service.transform({
|
|
2794
2794
|
callName: "transform",
|
|
2795
2795
|
refs,
|
|
2796
2796
|
input,
|
|
2797
2797
|
options: options || {},
|
|
2798
2798
|
isTTY: isTTY(),
|
|
2799
2799
|
fs: fsAsync,
|
|
2800
|
-
callback: (err, res) => err ? reject(err) :
|
|
2800
|
+
callback: (err, res) => err ? reject(err) : resolve7(res)
|
|
2801
2801
|
})),
|
|
2802
|
-
formatMessages: (messages, options) => new Promise((
|
|
2802
|
+
formatMessages: (messages, options) => new Promise((resolve7, reject) => service.formatMessages({
|
|
2803
2803
|
callName: "formatMessages",
|
|
2804
2804
|
refs,
|
|
2805
2805
|
messages,
|
|
2806
2806
|
options,
|
|
2807
|
-
callback: (err, res) => err ? reject(err) :
|
|
2807
|
+
callback: (err, res) => err ? reject(err) : resolve7(res)
|
|
2808
2808
|
})),
|
|
2809
|
-
analyzeMetafile: (metafile, options) => new Promise((
|
|
2809
|
+
analyzeMetafile: (metafile, options) => new Promise((resolve7, reject) => service.analyzeMetafile({
|
|
2810
2810
|
callName: "analyzeMetafile",
|
|
2811
2811
|
refs,
|
|
2812
2812
|
metafile: typeof metafile === "string" ? metafile : JSON.stringify(metafile),
|
|
2813
2813
|
options,
|
|
2814
|
-
callback: (err, res) => err ? reject(err) :
|
|
2814
|
+
callback: (err, res) => err ? reject(err) : resolve7(res)
|
|
2815
2815
|
}))
|
|
2816
2816
|
};
|
|
2817
2817
|
return longLivedService;
|
|
@@ -2889,13 +2889,13 @@ error: ${text}`);
|
|
|
2889
2889
|
worker.postMessage(msg);
|
|
2890
2890
|
let status = Atomics.wait(sharedBufferView, 0, 0);
|
|
2891
2891
|
if (status !== "ok" && status !== "not-equal") throw new Error("Internal error: Atomics.wait() failed: " + status);
|
|
2892
|
-
let { message: { id: id2, resolve:
|
|
2892
|
+
let { message: { id: id2, resolve: resolve7, reject, properties } } = worker_threads2.receiveMessageOnPort(mainPort);
|
|
2893
2893
|
if (id !== id2) throw new Error(`Internal error: Expected id ${id} but got id ${id2}`);
|
|
2894
2894
|
if (reject) {
|
|
2895
2895
|
applyProperties(reject, properties);
|
|
2896
2896
|
throw reject;
|
|
2897
2897
|
}
|
|
2898
|
-
return
|
|
2898
|
+
return resolve7;
|
|
2899
2899
|
};
|
|
2900
2900
|
worker.unref();
|
|
2901
2901
|
return {
|
|
@@ -2986,6 +2986,7 @@ error: ${text}`);
|
|
|
2986
2986
|
var DEFAULT_MAX_RESTARTS = 10;
|
|
2987
2987
|
var DEFAULT_WATCH_DEBOUNCE = 250;
|
|
2988
2988
|
var DEFAULT_MIN_UPTIME = 0;
|
|
2989
|
+
var DEFAULT_PM_LISTEN_TIMEOUT = 3e3;
|
|
2989
2990
|
var DEFAULT_HEALTHCHECK_GRACE_PERIOD = 5e3;
|
|
2990
2991
|
var DEFAULT_HEALTHCHECK_INTERVAL = 1e4;
|
|
2991
2992
|
var DEFAULT_HEALTHCHECK_TIMEOUT = 3e3;
|
|
@@ -2993,6 +2994,11 @@ error: ${text}`);
|
|
|
2993
2994
|
var DEFAULT_LOG_LINES = 40;
|
|
2994
2995
|
var DEFAULT_PM_STOP_POLL_MS = 100;
|
|
2995
2996
|
var DEFAULT_PM_STOP_GRACE_PERIOD_MS = 5e3;
|
|
2997
|
+
var DEFAULT_PM_KILL_TIMEOUT = DEFAULT_PM_STOP_GRACE_PERIOD_MS;
|
|
2998
|
+
var DEFAULT_PM_EXP_BACKOFF_MAX_DELAY = 15e3;
|
|
2999
|
+
var DEFAULT_PM_MEMORY_CHECK_INTERVAL = 500;
|
|
3000
|
+
var DEFAULT_PM_RESTART_WINDOW = 0;
|
|
3001
|
+
var DEFAULT_PM_PROXY_STRATEGY = "proxy";
|
|
2996
3002
|
var PM_WAPK_ONLINE_STDIN_SHUTDOWN_ENV = "ELIT_PM_WAPK_ONLINE_STDIN_SHUTDOWN";
|
|
2997
3003
|
var PM_WAPK_ONLINE_SHUTDOWN_COMMAND = "__ELIT_PM_WAPK_ONLINE_SHUTDOWN__";
|
|
2998
3004
|
var PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS = 8e3;
|
|
@@ -3030,6 +3036,32 @@ error: ${text}`);
|
|
|
3030
3036
|
}
|
|
3031
3037
|
throw new Error(`${optionName} must be one of: always, on-failure, never`);
|
|
3032
3038
|
}
|
|
3039
|
+
function normalizePmMemoryAction(value, optionName = "--memory-action") {
|
|
3040
|
+
if (value === void 0 || value === null || value === "") {
|
|
3041
|
+
return void 0;
|
|
3042
|
+
}
|
|
3043
|
+
if (typeof value !== "string") {
|
|
3044
|
+
throw new Error(`${optionName} must be one of: restart, stop`);
|
|
3045
|
+
}
|
|
3046
|
+
const action = value.trim().toLowerCase();
|
|
3047
|
+
if (action === "restart" || action === "stop") {
|
|
3048
|
+
return action;
|
|
3049
|
+
}
|
|
3050
|
+
throw new Error(`${optionName} must be one of: restart, stop`);
|
|
3051
|
+
}
|
|
3052
|
+
function normalizePmProxyStrategy(value, optionName = "--proxy-strategy") {
|
|
3053
|
+
if (value === void 0 || value === null || value === "") {
|
|
3054
|
+
return void 0;
|
|
3055
|
+
}
|
|
3056
|
+
if (typeof value !== "string") {
|
|
3057
|
+
throw new Error(`${optionName} must be one of: proxy, inherit`);
|
|
3058
|
+
}
|
|
3059
|
+
const strategy = value.trim().toLowerCase();
|
|
3060
|
+
if (strategy === "proxy" || strategy === "inherit") {
|
|
3061
|
+
return strategy;
|
|
3062
|
+
}
|
|
3063
|
+
throw new Error(`${optionName} must be one of: proxy, inherit`);
|
|
3064
|
+
}
|
|
3033
3065
|
function normalizeIntegerOption(value, optionName, min = 0) {
|
|
3034
3066
|
const parsed = Number.parseInt(value, 10);
|
|
3035
3067
|
if (!Number.isFinite(parsed) || parsed < min) {
|
|
@@ -3037,6 +3069,30 @@ error: ${text}`);
|
|
|
3037
3069
|
}
|
|
3038
3070
|
return parsed;
|
|
3039
3071
|
}
|
|
3072
|
+
function normalizePmMemoryLimit(value, optionName = "--max-memory") {
|
|
3073
|
+
if (value === void 0 || value === null || value === "") {
|
|
3074
|
+
return void 0;
|
|
3075
|
+
}
|
|
3076
|
+
if (typeof value === "number") {
|
|
3077
|
+
const normalized2 = Math.trunc(value);
|
|
3078
|
+
if (!Number.isFinite(normalized2) || normalized2 < 1) {
|
|
3079
|
+
throw new Error(`${optionName} must be a number >= 1 or a size like 256M.`);
|
|
3080
|
+
}
|
|
3081
|
+
return normalized2;
|
|
3082
|
+
}
|
|
3083
|
+
if (typeof value !== "string") {
|
|
3084
|
+
throw new Error(`${optionName} must be a number >= 1 or a size like 256M.`);
|
|
3085
|
+
}
|
|
3086
|
+
const normalized = value.trim();
|
|
3087
|
+
const match = /^(\d+)(b|kb|mb|gb|tb|k|m|g|t)?$/i.exec(normalized);
|
|
3088
|
+
if (!match) {
|
|
3089
|
+
throw new Error(`${optionName} must be a number >= 1 or a size like 256M.`);
|
|
3090
|
+
}
|
|
3091
|
+
const amount = normalizeIntegerOption(match[1] ?? "", optionName, 1);
|
|
3092
|
+
const unit = (match[2] ?? "b").toLowerCase();
|
|
3093
|
+
const multiplier = unit === "b" ? 1 : unit === "k" || unit === "kb" ? 1024 : unit === "m" || unit === "mb" ? 1024 ** 2 : unit === "g" || unit === "gb" ? 1024 ** 3 : 1024 ** 4;
|
|
3094
|
+
return amount * multiplier;
|
|
3095
|
+
}
|
|
3040
3096
|
function normalizeNonEmptyString(value) {
|
|
3041
3097
|
if (typeof value !== "string") {
|
|
3042
3098
|
return void 0;
|
|
@@ -3313,6 +3369,30 @@ error: ${text}`);
|
|
|
3313
3369
|
}
|
|
3314
3370
|
return value;
|
|
3315
3371
|
}
|
|
3372
|
+
function normalizePmProxyConfig(value, optionName = "pm proxy") {
|
|
3373
|
+
if (value === void 0 || value === null) {
|
|
3374
|
+
return void 0;
|
|
3375
|
+
}
|
|
3376
|
+
if (typeof value !== "object") {
|
|
3377
|
+
throw new Error(`${optionName} must be an object with at least a port.`);
|
|
3378
|
+
}
|
|
3379
|
+
const candidate = value;
|
|
3380
|
+
const hasAnyValue = candidate.port !== void 0 || candidate.host !== void 0 || candidate.targetHost !== void 0 || candidate.envVar !== void 0;
|
|
3381
|
+
if (!hasAnyValue) {
|
|
3382
|
+
return void 0;
|
|
3383
|
+
}
|
|
3384
|
+
if (candidate.port === void 0 || candidate.port === null || candidate.port === "") {
|
|
3385
|
+
throw new Error(`${optionName}.port is required.`);
|
|
3386
|
+
}
|
|
3387
|
+
const port = typeof candidate.port === "number" ? normalizeIntegerOption(String(Math.trunc(candidate.port)), `${optionName}.port`, 1) : normalizeIntegerOption(String(candidate.port), `${optionName}.port`, 1);
|
|
3388
|
+
return {
|
|
3389
|
+
port,
|
|
3390
|
+
strategy: normalizePmProxyStrategy(candidate.strategy, `${optionName}.strategy`) ?? DEFAULT_PM_PROXY_STRATEGY,
|
|
3391
|
+
host: normalizeNonEmptyString(candidate.host),
|
|
3392
|
+
targetHost: normalizeNonEmptyString(candidate.targetHost),
|
|
3393
|
+
envVar: normalizeNonEmptyString(candidate.envVar)
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3316
3396
|
|
|
3317
3397
|
// src/cli/pm/records.ts
|
|
3318
3398
|
var import_node_fs2 = __require("fs");
|
|
@@ -3345,18 +3425,31 @@ error: ${text}`);
|
|
|
3345
3425
|
function toSavedAppDefinition(record) {
|
|
3346
3426
|
return {
|
|
3347
3427
|
name: record.name,
|
|
3428
|
+
baseName: record.baseName,
|
|
3429
|
+
instanceIndex: record.instanceIndex,
|
|
3430
|
+
instances: record.instances,
|
|
3348
3431
|
type: record.type,
|
|
3349
3432
|
cwd: record.cwd,
|
|
3350
3433
|
runtime: record.runtime,
|
|
3351
3434
|
env: record.env,
|
|
3435
|
+
proxy: record.proxy,
|
|
3352
3436
|
script: record.script,
|
|
3353
3437
|
file: record.file,
|
|
3354
3438
|
wapk: record.wapk,
|
|
3355
3439
|
password: record.password,
|
|
3356
3440
|
wapkRun: record.wapkRun,
|
|
3357
3441
|
restartPolicy: record.restartPolicy,
|
|
3442
|
+
maxMemoryBytes: record.maxMemoryBytes,
|
|
3443
|
+
memoryAction: record.memoryAction,
|
|
3444
|
+
cronRestart: record.cronRestart,
|
|
3445
|
+
expBackoffRestartDelay: record.expBackoffRestartDelay,
|
|
3446
|
+
expBackoffRestartMaxDelay: record.expBackoffRestartMaxDelay,
|
|
3447
|
+
restartWindow: record.restartWindow,
|
|
3448
|
+
waitReady: record.waitReady,
|
|
3449
|
+
listenTimeout: record.listenTimeout,
|
|
3358
3450
|
autorestart: record.autorestart,
|
|
3359
3451
|
restartDelay: record.restartDelay,
|
|
3452
|
+
killTimeout: record.killTimeout,
|
|
3360
3453
|
maxRestarts: record.maxRestarts,
|
|
3361
3454
|
minUptime: record.minUptime,
|
|
3362
3455
|
watch: record.watch,
|
|
@@ -3423,6 +3516,9 @@ error: ${text}`);
|
|
|
3423
3516
|
}
|
|
3424
3517
|
return listPmRecordMatches(paths).find((match) => match.record.name === nameOrId);
|
|
3425
3518
|
}
|
|
3519
|
+
function findPmGroupMatches(paths, baseName) {
|
|
3520
|
+
return listPmRecordMatches(paths).filter((match) => match.record.baseName === baseName).sort((left, right) => left.record.instanceIndex - right.record.instanceIndex);
|
|
3521
|
+
}
|
|
3426
3522
|
function isProcessAlive(pid) {
|
|
3427
3523
|
if (!pid || pid <= 0) {
|
|
3428
3524
|
return false;
|
|
@@ -3472,11 +3568,22 @@ error: ${text}`);
|
|
|
3472
3568
|
runtime: record.runtime,
|
|
3473
3569
|
cwd: record.cwd,
|
|
3474
3570
|
env: record.env,
|
|
3571
|
+
proxy: record.proxy,
|
|
3572
|
+
instances: record.instances,
|
|
3475
3573
|
autorestart: record.autorestart,
|
|
3476
3574
|
restartDelay: record.restartDelay,
|
|
3575
|
+
killTimeout: record.killTimeout,
|
|
3477
3576
|
maxRestarts: record.maxRestarts,
|
|
3478
3577
|
password: record.password,
|
|
3479
3578
|
restartPolicy: record.restartPolicy,
|
|
3579
|
+
maxMemory: record.maxMemoryBytes,
|
|
3580
|
+
memoryAction: record.memoryAction,
|
|
3581
|
+
cronRestart: record.cronRestart,
|
|
3582
|
+
expBackoffRestartDelay: record.expBackoffRestartDelay,
|
|
3583
|
+
expBackoffRestartMaxDelay: record.expBackoffRestartMaxDelay,
|
|
3584
|
+
restartWindow: record.restartWindow,
|
|
3585
|
+
waitReady: record.waitReady,
|
|
3586
|
+
listenTimeout: record.listenTimeout,
|
|
3480
3587
|
minUptime: record.minUptime,
|
|
3481
3588
|
watch: record.watch,
|
|
3482
3589
|
watchPaths: record.watchPaths,
|
|
@@ -3495,11 +3602,22 @@ error: ${text}`);
|
|
|
3495
3602
|
runtime: app.runtime,
|
|
3496
3603
|
cwd: app.cwd,
|
|
3497
3604
|
env: app.env,
|
|
3605
|
+
proxy: app.proxy,
|
|
3606
|
+
instances: app.instances,
|
|
3498
3607
|
autorestart: app.autorestart,
|
|
3499
3608
|
restartDelay: app.restartDelay,
|
|
3609
|
+
killTimeout: app.killTimeout,
|
|
3500
3610
|
maxRestarts: app.maxRestarts,
|
|
3501
3611
|
password: app.password,
|
|
3502
3612
|
restartPolicy: app.restartPolicy,
|
|
3613
|
+
maxMemory: app.maxMemoryBytes,
|
|
3614
|
+
memoryAction: app.memoryAction,
|
|
3615
|
+
cronRestart: app.cronRestart,
|
|
3616
|
+
expBackoffRestartDelay: app.expBackoffRestartDelay,
|
|
3617
|
+
expBackoffRestartMaxDelay: app.expBackoffRestartMaxDelay,
|
|
3618
|
+
restartWindow: app.restartWindow,
|
|
3619
|
+
waitReady: app.waitReady,
|
|
3620
|
+
listenTimeout: app.listenTimeout,
|
|
3503
3621
|
minUptime: app.minUptime,
|
|
3504
3622
|
watch: app.watch,
|
|
3505
3623
|
watchPaths: app.watchPaths,
|
|
@@ -3511,6 +3629,170 @@ error: ${text}`);
|
|
|
3511
3629
|
|
|
3512
3630
|
// src/cli/pm/config.ts
|
|
3513
3631
|
var import_node_path4 = __require("path");
|
|
3632
|
+
|
|
3633
|
+
// src/cli/pm/schedule.ts
|
|
3634
|
+
var PM_EVERY_PATTERN = /^@every\s+(\d+)(ms|s|m|h|d)$/i;
|
|
3635
|
+
function parsePositiveInteger(value, optionName, min = 0) {
|
|
3636
|
+
const parsed = Number.parseInt(value, 10);
|
|
3637
|
+
if (!Number.isFinite(parsed) || parsed < min) {
|
|
3638
|
+
throw new Error(`${optionName} must be a number >= ${min}`);
|
|
3639
|
+
}
|
|
3640
|
+
return parsed;
|
|
3641
|
+
}
|
|
3642
|
+
function fillRange(values, start, end, step, min, max, optionName) {
|
|
3643
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < min || end > max || start > end) {
|
|
3644
|
+
throw new Error(`${optionName} contains an invalid range: ${start}-${end}`);
|
|
3645
|
+
}
|
|
3646
|
+
if (!Number.isInteger(step) || step < 1) {
|
|
3647
|
+
throw new Error(`${optionName} contains an invalid step: ${step}`);
|
|
3648
|
+
}
|
|
3649
|
+
for (let current = start; current <= end; current += step) {
|
|
3650
|
+
values.add(current);
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
function parseCronSegment(segment, values, min, max, optionName) {
|
|
3654
|
+
const normalized = segment.trim();
|
|
3655
|
+
if (!normalized) {
|
|
3656
|
+
throw new Error(`${optionName} contains an empty field segment.`);
|
|
3657
|
+
}
|
|
3658
|
+
const [rangeToken, stepToken] = normalized.split("/");
|
|
3659
|
+
if (normalized.split("/").length > 2) {
|
|
3660
|
+
throw new Error(`${optionName} contains an invalid step segment: ${normalized}`);
|
|
3661
|
+
}
|
|
3662
|
+
const step = stepToken === void 0 ? 1 : parsePositiveInteger(stepToken, optionName, 1);
|
|
3663
|
+
if (rangeToken === "*") {
|
|
3664
|
+
fillRange(values, min, max, step, min, max, optionName);
|
|
3665
|
+
return;
|
|
3666
|
+
}
|
|
3667
|
+
if (rangeToken.includes("-")) {
|
|
3668
|
+
const [startToken, endToken] = rangeToken.split("-");
|
|
3669
|
+
if (!startToken || !endToken) {
|
|
3670
|
+
throw new Error(`${optionName} contains an invalid range: ${normalized}`);
|
|
3671
|
+
}
|
|
3672
|
+
const start2 = parsePositiveInteger(startToken, optionName, min);
|
|
3673
|
+
const end = parsePositiveInteger(endToken, optionName, min);
|
|
3674
|
+
fillRange(values, start2, end, step, min, max, optionName);
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
const start = parsePositiveInteger(rangeToken, optionName, min);
|
|
3678
|
+
fillRange(values, start, stepToken === void 0 ? start : max, step, min, max, optionName);
|
|
3679
|
+
}
|
|
3680
|
+
function parseCronField(token, min, max, optionName) {
|
|
3681
|
+
const normalized = token.trim();
|
|
3682
|
+
if (!normalized) {
|
|
3683
|
+
throw new Error(`${optionName} contains an empty field.`);
|
|
3684
|
+
}
|
|
3685
|
+
if (normalized === "*") {
|
|
3686
|
+
const values2 = /* @__PURE__ */ new Set();
|
|
3687
|
+
fillRange(values2, min, max, 1, min, max, optionName);
|
|
3688
|
+
return {
|
|
3689
|
+
values: values2,
|
|
3690
|
+
wildcard: true
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
const values = /* @__PURE__ */ new Set();
|
|
3694
|
+
for (const segment of normalized.split(",")) {
|
|
3695
|
+
parseCronSegment(segment, values, min, max, optionName);
|
|
3696
|
+
}
|
|
3697
|
+
if (values.size === 0) {
|
|
3698
|
+
throw new Error(`${optionName} must match at least one value.`);
|
|
3699
|
+
}
|
|
3700
|
+
return {
|
|
3701
|
+
values,
|
|
3702
|
+
wildcard: false
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
function matchesCronField(field, value) {
|
|
3706
|
+
if (!field) {
|
|
3707
|
+
return true;
|
|
3708
|
+
}
|
|
3709
|
+
return field.wildcard || field.values.has(value);
|
|
3710
|
+
}
|
|
3711
|
+
function matchesCronDate(schedule, candidate) {
|
|
3712
|
+
if (schedule.kind !== "cron") {
|
|
3713
|
+
return false;
|
|
3714
|
+
}
|
|
3715
|
+
const secondMatches = schedule.hasSeconds ? matchesCronField(schedule.seconds, candidate.getSeconds()) : true;
|
|
3716
|
+
const minuteMatches = matchesCronField(schedule.minutes, candidate.getMinutes());
|
|
3717
|
+
const hourMatches = matchesCronField(schedule.hours, candidate.getHours());
|
|
3718
|
+
const monthMatches = matchesCronField(schedule.months, candidate.getMonth() + 1);
|
|
3719
|
+
const dayOfMonthMatches = matchesCronField(schedule.daysOfMonth, candidate.getDate());
|
|
3720
|
+
const dayOfWeekMatches = matchesCronField(schedule.daysOfWeek, candidate.getDay());
|
|
3721
|
+
const domWildcard = schedule.daysOfMonth?.wildcard ?? true;
|
|
3722
|
+
const dowWildcard = schedule.daysOfWeek?.wildcard ?? true;
|
|
3723
|
+
const dayMatches = domWildcard && dowWildcard ? true : domWildcard ? dayOfWeekMatches : dowWildcard ? dayOfMonthMatches : dayOfMonthMatches || dayOfWeekMatches;
|
|
3724
|
+
return secondMatches && minuteMatches && hourMatches && monthMatches && dayMatches;
|
|
3725
|
+
}
|
|
3726
|
+
function parsePmRestartSchedule(expression, optionName = "--cron-restart") {
|
|
3727
|
+
const normalized = expression.trim();
|
|
3728
|
+
const everyMatch = PM_EVERY_PATTERN.exec(normalized);
|
|
3729
|
+
if (everyMatch) {
|
|
3730
|
+
const [, valueText, unit] = everyMatch;
|
|
3731
|
+
const value = parsePositiveInteger(valueText ?? "", optionName, 1);
|
|
3732
|
+
const multiplier = unit?.toLowerCase() === "ms" ? 1 : unit?.toLowerCase() === "s" ? 1e3 : unit?.toLowerCase() === "m" ? 6e4 : unit?.toLowerCase() === "h" ? 36e5 : 864e5;
|
|
3733
|
+
return {
|
|
3734
|
+
expression: normalized,
|
|
3735
|
+
kind: "every",
|
|
3736
|
+
intervalMs: value * multiplier,
|
|
3737
|
+
hasSeconds: true
|
|
3738
|
+
};
|
|
3739
|
+
}
|
|
3740
|
+
const fields = normalized.split(/\s+/).filter((field) => field.length > 0);
|
|
3741
|
+
if (fields.length !== 5 && fields.length !== 6) {
|
|
3742
|
+
throw new Error(`${optionName} must be a 5-field cron expression or @every <duration>.`);
|
|
3743
|
+
}
|
|
3744
|
+
const hasSeconds = fields.length === 6;
|
|
3745
|
+
const offset = hasSeconds ? 1 : 0;
|
|
3746
|
+
return {
|
|
3747
|
+
expression: normalized,
|
|
3748
|
+
kind: "cron",
|
|
3749
|
+
hasSeconds,
|
|
3750
|
+
seconds: hasSeconds ? parseCronField(fields[0] ?? "", 0, 59, optionName) : void 0,
|
|
3751
|
+
minutes: parseCronField(fields[0 + offset] ?? "", 0, 59, optionName),
|
|
3752
|
+
hours: parseCronField(fields[1 + offset] ?? "", 0, 23, optionName),
|
|
3753
|
+
daysOfMonth: parseCronField(fields[2 + offset] ?? "", 1, 31, optionName),
|
|
3754
|
+
months: parseCronField(fields[3 + offset] ?? "", 1, 12, optionName),
|
|
3755
|
+
daysOfWeek: parseCronField(fields[4 + offset] ?? "", 0, 6, optionName)
|
|
3756
|
+
};
|
|
3757
|
+
}
|
|
3758
|
+
function normalizePmRestartSchedule(value, optionName = "--cron-restart") {
|
|
3759
|
+
if (value === void 0 || value === null || value === "") {
|
|
3760
|
+
return void 0;
|
|
3761
|
+
}
|
|
3762
|
+
if (typeof value !== "string") {
|
|
3763
|
+
throw new Error(`${optionName} must be a cron string or @every <duration>.`);
|
|
3764
|
+
}
|
|
3765
|
+
const normalized = value.trim();
|
|
3766
|
+
if (!normalized) {
|
|
3767
|
+
throw new Error(`${optionName} must be a non-empty cron string or @every <duration>.`);
|
|
3768
|
+
}
|
|
3769
|
+
parsePmRestartSchedule(normalized, optionName);
|
|
3770
|
+
return normalized;
|
|
3771
|
+
}
|
|
3772
|
+
function resolveNextPmScheduleOccurrence(schedule, after = /* @__PURE__ */ new Date()) {
|
|
3773
|
+
if (schedule.kind === "every") {
|
|
3774
|
+
return new Date(after.getTime() + (schedule.intervalMs ?? 0));
|
|
3775
|
+
}
|
|
3776
|
+
const stepMs = schedule.hasSeconds ? 1e3 : 6e4;
|
|
3777
|
+
const candidate = new Date(after.getTime());
|
|
3778
|
+
if (schedule.hasSeconds) {
|
|
3779
|
+
candidate.setMilliseconds(0);
|
|
3780
|
+
candidate.setSeconds(candidate.getSeconds() + 1);
|
|
3781
|
+
} else {
|
|
3782
|
+
candidate.setSeconds(0, 0);
|
|
3783
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
3784
|
+
}
|
|
3785
|
+
const maxIterations = schedule.hasSeconds ? 2678400 : 527040;
|
|
3786
|
+
for (let index = 0; index < maxIterations; index++) {
|
|
3787
|
+
if (matchesCronDate(schedule, candidate)) {
|
|
3788
|
+
return new Date(candidate.getTime());
|
|
3789
|
+
}
|
|
3790
|
+
candidate.setTime(candidate.getTime() + stepMs);
|
|
3791
|
+
}
|
|
3792
|
+
return void 0;
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
// src/cli/pm/config.ts
|
|
3514
3796
|
function parsePmTarget(parsed, workspaceRoot) {
|
|
3515
3797
|
if (parsed.script) {
|
|
3516
3798
|
return { script: parsed.script };
|
|
@@ -3553,6 +3835,20 @@ error: ${text}`);
|
|
|
3553
3835
|
}
|
|
3554
3836
|
return "process";
|
|
3555
3837
|
}
|
|
3838
|
+
function formatPmInstanceName(baseName, instanceIndex) {
|
|
3839
|
+
return instanceIndex <= 1 ? baseName : `${baseName}:${instanceIndex}`;
|
|
3840
|
+
}
|
|
3841
|
+
function expandPmInstanceDefinitions(definition, targetInstances = definition.instances) {
|
|
3842
|
+
return Array.from({ length: targetInstances }, (_, index) => {
|
|
3843
|
+
const instanceIndex = index + 1;
|
|
3844
|
+
return {
|
|
3845
|
+
...definition,
|
|
3846
|
+
name: formatPmInstanceName(definition.baseName, instanceIndex),
|
|
3847
|
+
instanceIndex,
|
|
3848
|
+
instances: targetInstances
|
|
3849
|
+
};
|
|
3850
|
+
});
|
|
3851
|
+
}
|
|
3556
3852
|
function countDefinedTargets(app) {
|
|
3557
3853
|
return [app.script, app.file, app.wapk].filter(Boolean).length;
|
|
3558
3854
|
}
|
|
@@ -3590,8 +3886,25 @@ error: ${text}`);
|
|
|
3590
3886
|
throw new Error("A pm app must define exactly one of script, file, or wapk.");
|
|
3591
3887
|
}
|
|
3592
3888
|
const name = defaultProcessName({ script, file, wapk }, parsed.name ?? base?.name);
|
|
3889
|
+
const instances = parsed.instances ?? base?.instances ?? 1;
|
|
3890
|
+
if (!Number.isInteger(instances) || instances < 1) {
|
|
3891
|
+
throw new Error("pm instances must be a number >= 1.");
|
|
3892
|
+
}
|
|
3893
|
+
const proxy = normalizePmProxyConfig(
|
|
3894
|
+
parsed.proxy ? { ...base?.proxy ?? {}, ...parsed.proxy } : base?.proxy,
|
|
3895
|
+
parsed.proxy ? "pm proxy" : "pm.apps[].proxy"
|
|
3896
|
+
);
|
|
3897
|
+
if (proxy?.strategy === "inherit" && instances > 1) {
|
|
3898
|
+
throw new Error('pm proxy strategy "inherit" currently supports only one managed instance per app.');
|
|
3899
|
+
}
|
|
3593
3900
|
const mergedWapkRun = mergePmWapkRunConfig(base?.wapkRun, parsed.wapkRun);
|
|
3594
3901
|
const runtime2 = normalizePmRuntime(parsed.runtime ?? mergedWapkRun?.runtime ?? base?.runtime, "--runtime");
|
|
3902
|
+
const maxMemoryBytes = parsed.maxMemoryBytes ?? normalizePmMemoryLimit(base?.maxMemory, "pm.apps[].maxMemory");
|
|
3903
|
+
const memoryAction = parsed.memoryAction ?? normalizePmMemoryAction(base?.memoryAction, "pm.apps[].memoryAction") ?? "restart";
|
|
3904
|
+
const cronRestart = parsed.cronRestart ?? normalizePmRestartSchedule(base?.cronRestart, "pm.apps[].cronRestart");
|
|
3905
|
+
const expBackoffRestartDelay = parsed.expBackoffRestartDelay ?? (base?.expBackoffRestartDelay === void 0 ? void 0 : normalizeIntegerOption(String(base.expBackoffRestartDelay), "pm.apps[].expBackoffRestartDelay", 1));
|
|
3906
|
+
const expBackoffRestartMaxDelay = parsed.expBackoffRestartMaxDelay ?? (base?.expBackoffRestartMaxDelay === void 0 ? void 0 : normalizeIntegerOption(String(base.expBackoffRestartMaxDelay), "pm.apps[].expBackoffRestartMaxDelay", 1));
|
|
3907
|
+
const restartWindow = parsed.restartWindow ?? (base?.restartWindow === void 0 ? void 0 : normalizeIntegerOption(String(base.restartWindow), "pm.apps[].restartWindow", 1));
|
|
3595
3908
|
let restartPolicy = normalizePmRestartPolicy(parsed.restartPolicy ?? base?.restartPolicy, "--restart-policy") ?? (base?.autorestart ?? true ? "always" : "never");
|
|
3596
3909
|
if (parsed.autorestart === false) {
|
|
3597
3910
|
restartPolicy = "never";
|
|
@@ -3611,6 +3924,7 @@ error: ${text}`);
|
|
|
3611
3924
|
timeout: parsed.healthCheckTimeout,
|
|
3612
3925
|
maxFailures: parsed.healthCheckMaxFailures
|
|
3613
3926
|
} : base?.healthCheck);
|
|
3927
|
+
const waitReady = parsed.waitReady ?? base?.waitReady ?? false;
|
|
3614
3928
|
const password = parsed.password ?? mergedWapkRun?.password ?? base?.password;
|
|
3615
3929
|
const wapkRun = stripPmWapkSourceFromRunConfig(mergedWapkRun);
|
|
3616
3930
|
if (password && !wapk) {
|
|
@@ -3619,8 +3933,14 @@ error: ${text}`);
|
|
|
3619
3933
|
if (wapkRun && !wapk) {
|
|
3620
3934
|
throw new Error("WAPK run options are only supported when starting a WAPK app.");
|
|
3621
3935
|
}
|
|
3936
|
+
if (waitReady && !healthCheck) {
|
|
3937
|
+
throw new Error("--wait-ready requires --health-url or pm.apps[].healthCheck.");
|
|
3938
|
+
}
|
|
3622
3939
|
return {
|
|
3623
|
-
name,
|
|
3940
|
+
name: formatPmInstanceName(name, 1),
|
|
3941
|
+
baseName: name,
|
|
3942
|
+
instanceIndex: 1,
|
|
3943
|
+
instances,
|
|
3624
3944
|
type: script ? "script" : wapk ? "wapk" : "file",
|
|
3625
3945
|
source,
|
|
3626
3946
|
cwd: resolvedCwd,
|
|
@@ -3635,9 +3955,19 @@ error: ${text}`);
|
|
|
3635
3955
|
wapkRun,
|
|
3636
3956
|
autorestart,
|
|
3637
3957
|
restartDelay: parsed.restartDelay ?? base?.restartDelay ?? DEFAULT_RESTART_DELAY,
|
|
3958
|
+
proxy,
|
|
3959
|
+
killTimeout: parsed.killTimeout ?? base?.killTimeout ?? DEFAULT_PM_KILL_TIMEOUT,
|
|
3638
3960
|
maxRestarts: parsed.maxRestarts ?? base?.maxRestarts ?? DEFAULT_MAX_RESTARTS,
|
|
3639
3961
|
password,
|
|
3640
3962
|
restartPolicy,
|
|
3963
|
+
maxMemoryBytes,
|
|
3964
|
+
memoryAction,
|
|
3965
|
+
cronRestart,
|
|
3966
|
+
expBackoffRestartDelay,
|
|
3967
|
+
expBackoffRestartMaxDelay,
|
|
3968
|
+
restartWindow,
|
|
3969
|
+
waitReady,
|
|
3970
|
+
listenTimeout: parsed.listenTimeout ?? base?.listenTimeout ?? DEFAULT_PM_LISTEN_TIMEOUT,
|
|
3641
3971
|
minUptime: parsed.minUptime ?? base?.minUptime ?? DEFAULT_MIN_UPTIME,
|
|
3642
3972
|
watch: watch2,
|
|
3643
3973
|
watchPaths: watch2 ? normalizeResolvedWatchPaths(configuredWatchPaths, resolvedCwd, script ? "script" : wapk ? "wapk" : "file", file, wapk) : [],
|
|
@@ -3649,16 +3979,17 @@ error: ${text}`);
|
|
|
3649
3979
|
function resolvePmStartDefinitions(parsed, config, workspaceRoot) {
|
|
3650
3980
|
const configApps = getConfiguredPmApps(config);
|
|
3651
3981
|
const selection = resolveStartSelection(configApps, parsed, workspaceRoot);
|
|
3982
|
+
const expandDefinitions = (definitions) => definitions.flatMap((definition) => expandPmInstanceDefinitions(definition));
|
|
3652
3983
|
if (selection.startAll) {
|
|
3653
3984
|
if (configApps.length === 0) {
|
|
3654
3985
|
throw new Error("No pm apps configured in elit.config.* and no start target was provided.");
|
|
3655
3986
|
}
|
|
3656
|
-
return configApps.map((app) => resolvePmAppDefinition(app, { ...parsed, name: app.name }, workspaceRoot, "config"));
|
|
3987
|
+
return expandDefinitions(configApps.map((app) => resolvePmAppDefinition(app, { ...parsed, name: app.name }, workspaceRoot, "config")));
|
|
3657
3988
|
}
|
|
3658
3989
|
if (selection.selected) {
|
|
3659
|
-
return [resolvePmAppDefinition(selection.selected, parsed, workspaceRoot, "config")];
|
|
3990
|
+
return expandDefinitions([resolvePmAppDefinition(selection.selected, parsed, workspaceRoot, "config")]);
|
|
3660
3991
|
}
|
|
3661
|
-
return [resolvePmAppDefinition(void 0, parsed, workspaceRoot, "cli")];
|
|
3992
|
+
return expandDefinitions([resolvePmAppDefinition(void 0, parsed, workspaceRoot, "cli")]);
|
|
3662
3993
|
}
|
|
3663
3994
|
function parsePmStartArgs(args) {
|
|
3664
3995
|
const parsed = {
|
|
@@ -3731,6 +4062,39 @@ error: ${text}`);
|
|
|
3731
4062
|
parsed.env[key] = value;
|
|
3732
4063
|
break;
|
|
3733
4064
|
}
|
|
4065
|
+
case "--instances":
|
|
4066
|
+
parsed.instances = normalizeIntegerOption(readRequiredValue(args, ++index, "--instances"), "--instances", 1);
|
|
4067
|
+
break;
|
|
4068
|
+
case "--proxy-port":
|
|
4069
|
+
parsed.proxy = {
|
|
4070
|
+
...parsed.proxy,
|
|
4071
|
+
port: normalizeIntegerOption(readRequiredValue(args, ++index, "--proxy-port"), "--proxy-port", 1)
|
|
4072
|
+
};
|
|
4073
|
+
break;
|
|
4074
|
+
case "--proxy-strategy":
|
|
4075
|
+
parsed.proxy = {
|
|
4076
|
+
...parsed.proxy,
|
|
4077
|
+
strategy: normalizePmProxyStrategy(readRequiredValue(args, ++index, "--proxy-strategy"), "--proxy-strategy")
|
|
4078
|
+
};
|
|
4079
|
+
break;
|
|
4080
|
+
case "--proxy-host":
|
|
4081
|
+
parsed.proxy = {
|
|
4082
|
+
...parsed.proxy,
|
|
4083
|
+
host: readRequiredValue(args, ++index, "--proxy-host")
|
|
4084
|
+
};
|
|
4085
|
+
break;
|
|
4086
|
+
case "--proxy-target-host":
|
|
4087
|
+
parsed.proxy = {
|
|
4088
|
+
...parsed.proxy,
|
|
4089
|
+
targetHost: readRequiredValue(args, ++index, "--proxy-target-host")
|
|
4090
|
+
};
|
|
4091
|
+
break;
|
|
4092
|
+
case "--proxy-env":
|
|
4093
|
+
parsed.proxy = {
|
|
4094
|
+
...parsed.proxy,
|
|
4095
|
+
envVar: readRequiredValue(args, ++index, "--proxy-env")
|
|
4096
|
+
};
|
|
4097
|
+
break;
|
|
3734
4098
|
case "--password":
|
|
3735
4099
|
parsed.password = readRequiredValue(args, ++index, "--password");
|
|
3736
4100
|
break;
|
|
@@ -3781,6 +4145,30 @@ error: ${text}`);
|
|
|
3781
4145
|
case "--restart-policy":
|
|
3782
4146
|
parsed.restartPolicy = normalizePmRestartPolicy(readRequiredValue(args, ++index, "--restart-policy"));
|
|
3783
4147
|
break;
|
|
4148
|
+
case "--max-memory":
|
|
4149
|
+
parsed.maxMemoryBytes = normalizePmMemoryLimit(readRequiredValue(args, ++index, "--max-memory"), "--max-memory");
|
|
4150
|
+
break;
|
|
4151
|
+
case "--memory-action":
|
|
4152
|
+
parsed.memoryAction = normalizePmMemoryAction(readRequiredValue(args, ++index, "--memory-action"), "--memory-action");
|
|
4153
|
+
break;
|
|
4154
|
+
case "--cron-restart":
|
|
4155
|
+
parsed.cronRestart = normalizePmRestartSchedule(readRequiredValue(args, ++index, "--cron-restart"), "--cron-restart");
|
|
4156
|
+
break;
|
|
4157
|
+
case "--exp-backoff-restart-delay":
|
|
4158
|
+
parsed.expBackoffRestartDelay = normalizeIntegerOption(readRequiredValue(args, ++index, "--exp-backoff-restart-delay"), "--exp-backoff-restart-delay", 1);
|
|
4159
|
+
break;
|
|
4160
|
+
case "--exp-backoff-restart-max-delay":
|
|
4161
|
+
parsed.expBackoffRestartMaxDelay = normalizeIntegerOption(readRequiredValue(args, ++index, "--exp-backoff-restart-max-delay"), "--exp-backoff-restart-max-delay", 1);
|
|
4162
|
+
break;
|
|
4163
|
+
case "--restart-window":
|
|
4164
|
+
parsed.restartWindow = normalizeIntegerOption(readRequiredValue(args, ++index, "--restart-window"), "--restart-window", 1);
|
|
4165
|
+
break;
|
|
4166
|
+
case "--wait-ready":
|
|
4167
|
+
parsed.waitReady = true;
|
|
4168
|
+
break;
|
|
4169
|
+
case "--listen-timeout":
|
|
4170
|
+
parsed.listenTimeout = normalizeIntegerOption(readRequiredValue(args, ++index, "--listen-timeout"), "--listen-timeout", 1);
|
|
4171
|
+
break;
|
|
3784
4172
|
case "--min-uptime":
|
|
3785
4173
|
parsed.minUptime = normalizeIntegerOption(readRequiredValue(args, ++index, "--min-uptime"), "--min-uptime");
|
|
3786
4174
|
break;
|
|
@@ -3820,6 +4208,9 @@ error: ${text}`);
|
|
|
3820
4208
|
case "--restart-delay":
|
|
3821
4209
|
parsed.restartDelay = normalizeIntegerOption(readRequiredValue(args, ++index, "--restart-delay"), "--restart-delay");
|
|
3822
4210
|
break;
|
|
4211
|
+
case "--kill-timeout":
|
|
4212
|
+
parsed.killTimeout = normalizeIntegerOption(readRequiredValue(args, ++index, "--kill-timeout"), "--kill-timeout", 1);
|
|
4213
|
+
break;
|
|
3823
4214
|
case "--max-restarts":
|
|
3824
4215
|
parsed.maxRestarts = normalizeIntegerOption(readRequiredValue(args, ++index, "--max-restarts"), "--max-restarts");
|
|
3825
4216
|
break;
|
|
@@ -3861,8 +4252,15 @@ error: ${text}`);
|
|
|
3861
4252
|
" elit pm start my-app",
|
|
3862
4253
|
" elit pm start",
|
|
3863
4254
|
" elit pm list",
|
|
4255
|
+
" elit pm list --json",
|
|
4256
|
+
" elit pm show <name>",
|
|
4257
|
+
" elit pm describe <name> --json",
|
|
3864
4258
|
" elit pm stop <name|all>",
|
|
3865
4259
|
" elit pm restart <name|all>",
|
|
4260
|
+
" elit pm reload <name|all>",
|
|
4261
|
+
" elit pm scale <name> <count>",
|
|
4262
|
+
" elit pm reset <name|all>",
|
|
4263
|
+
" elit pm send-signal <signal> <name|all>",
|
|
3866
4264
|
" elit pm delete <name|all>",
|
|
3867
4265
|
" elit pm save",
|
|
3868
4266
|
" elit pm resurrect",
|
|
@@ -3878,6 +4276,12 @@ error: ${text}`);
|
|
|
3878
4276
|
" --google-drive-shared-drive Forward supportsAllDrives=true for shared drives",
|
|
3879
4277
|
" --runtime, -r <name> Runtime override: node, bun, deno",
|
|
3880
4278
|
" --name, -n <name> Process name used by list/stop/restart",
|
|
4279
|
+
" --instances <count> Start multiple managed instances for the same app name",
|
|
4280
|
+
" --proxy-port <port> Own a public HTTP port through a PM proxy for single-instance reload handoff",
|
|
4281
|
+
" --proxy-strategy <mode> Public socket mode: proxy or inherit (default: proxy)",
|
|
4282
|
+
" --proxy-host <host> Public host bound by the PM proxy (default: 0.0.0.0)",
|
|
4283
|
+
" --proxy-target-host <host> Internal host used for upstream child traffic (default: 127.0.0.1)",
|
|
4284
|
+
" --proxy-env <name> Env var populated with the child private port (default: PORT)",
|
|
3881
4285
|
" --cwd <dir> Working directory for the managed process",
|
|
3882
4286
|
" --env KEY=VALUE Add or override an environment variable",
|
|
3883
4287
|
" --password <value> Password for locked WAPK archives",
|
|
@@ -3889,6 +4293,14 @@ error: ${text}`);
|
|
|
3889
4293
|
" --no-archive-watch Disable archive-source read sync for WAPK apps",
|
|
3890
4294
|
" --archive-sync-interval <ms> Forward WAPK archive read-sync interval (>= 50ms)",
|
|
3891
4295
|
" --restart-policy <mode> Restart policy: always, on-failure, never",
|
|
4296
|
+
" --max-memory <bytes|size> Trigger a memory action after a limit like 268435456 or 256M",
|
|
4297
|
+
" --memory-action <mode> Action on max-memory: restart, stop",
|
|
4298
|
+
" --cron-restart <expr> Restart on a cron schedule or @every <duration>",
|
|
4299
|
+
" --exp-backoff-restart-delay <ms> Exponential unstable-restart backoff base delay",
|
|
4300
|
+
" --exp-backoff-restart-max-delay <ms> Maximum unstable-restart backoff delay (default 15000)",
|
|
4301
|
+
" --restart-window <ms> Rolling time window used when counting restart attempts",
|
|
4302
|
+
" --wait-ready Keep the process in starting state until its health check succeeds",
|
|
4303
|
+
" --listen-timeout <ms> Startup wait limit when --wait-ready is enabled (default 3000)",
|
|
3892
4304
|
" --min-uptime <ms> Reset crash counter after this healthy uptime",
|
|
3893
4305
|
" --watch Restart when watched files change",
|
|
3894
4306
|
" --watch-path <path> Add a file or directory to watch",
|
|
@@ -3901,6 +4313,7 @@ error: ${text}`);
|
|
|
3901
4313
|
" --health-max-failures <n> Consecutive failures before restart (default 3)",
|
|
3902
4314
|
" --no-autorestart Disable automatic restart",
|
|
3903
4315
|
" --restart-delay <ms> Delay between restart attempts (default 1000)",
|
|
4316
|
+
" --kill-timeout <ms> Grace period before force-killing a stop or restart (default 5000)",
|
|
3904
4317
|
" --max-restarts <count> Maximum restart attempts (default 10)",
|
|
3905
4318
|
"",
|
|
3906
4319
|
"Config:",
|
|
@@ -3924,6 +4337,17 @@ error: ${text}`);
|
|
|
3924
4337
|
" - elit pm save persists running apps to pm.dumpFile or ./.elit/pm/dump.json.",
|
|
3925
4338
|
" - elit pm resurrect restarts whatever was last saved by elit pm save.",
|
|
3926
4339
|
" - elit pm start <name> starts a configured app by name.",
|
|
4340
|
+
" - elit pm reload <name|all> performs a rolling stop/start and waits for each instance to return online before continuing.",
|
|
4341
|
+
" - elit pm scale <name> <count> changes the number of managed instances for a running app group.",
|
|
4342
|
+
" - elit pm reset <name|all> clears restart count, last exit code, and saved error metadata.",
|
|
4343
|
+
" - elit pm send-signal <signal> <name|all> forwards a POSIX-style signal such as SIGUSR2 or TERM.",
|
|
4344
|
+
" - maxMemory works with memoryAction=restart|stop, cronRestart accepts cron or @every schedules, expBackoffRestartDelay doubles unstable restart delays, expBackoffRestartMaxDelay caps them, and restartWindow limits how long restart attempts keep counting toward maxRestarts.",
|
|
4345
|
+
" - killTimeout controls how long PM waits before force-killing an app that ignores stop or restart requests.",
|
|
4346
|
+
" - waitReady uses the configured health check as a startup gate and errors if it never becomes healthy within listenTimeout.",
|
|
4347
|
+
" - elit pm list shows live cpu, memory, and uptime columns when the child process is running.",
|
|
4348
|
+
" - elit pm list --json and elit pm jlist print machine-readable process state.",
|
|
4349
|
+
" - elit pm list --json, elit pm show --json, and elit pm describe --json include liveMetrics when available.",
|
|
4350
|
+
" - elit pm show <name> and elit pm describe <name> expose the full saved process record.",
|
|
3927
4351
|
" - TypeScript files with runtime node require tsx, otherwise use --runtime bun.",
|
|
3928
4352
|
" - WAPK processes are executed through elit wapk run inside the manager.",
|
|
3929
4353
|
" - WAPK PM apps can use local archives, gdrive://<fileId>, or pm.apps[].wapkRun.googleDrive.",
|
|
@@ -3979,16 +4403,94 @@ error: ${text}`);
|
|
|
3979
4403
|
const globalCommand = process.platform === "win32" ? "tsx.cmd" : "tsx";
|
|
3980
4404
|
return commandExists(globalCommand) ? globalCommand : void 0;
|
|
3981
4405
|
}
|
|
4406
|
+
function resolvePmNodeSharedListenerBootstrapPath() {
|
|
4407
|
+
const cliEntry = process.argv[1];
|
|
4408
|
+
const candidates = [
|
|
4409
|
+
(0, import_node_path5.join)(__dirname, "node-shared-listener-bootstrap.cjs"),
|
|
4410
|
+
(0, import_node_path5.join)(__dirname, "..", "..", "..", "dist", "pm-node-shared-listener-bootstrap.cjs")
|
|
4411
|
+
];
|
|
4412
|
+
if (cliEntry) {
|
|
4413
|
+
const resolvedCliEntry = (0, import_node_path5.resolve)(cliEntry);
|
|
4414
|
+
const baseDir = (0, import_node_path5.dirname)(resolvedCliEntry);
|
|
4415
|
+
candidates.push(
|
|
4416
|
+
(0, import_node_path5.join)(baseDir, "cli", "pm", "node-shared-listener-bootstrap.cjs"),
|
|
4417
|
+
(0, import_node_path5.join)(baseDir, "pm-node-shared-listener-bootstrap.cjs")
|
|
4418
|
+
);
|
|
4419
|
+
}
|
|
4420
|
+
return candidates.find((candidate) => (0, import_node_fs3.existsSync)(candidate));
|
|
4421
|
+
}
|
|
4422
|
+
function appendNodeOption(existing, option) {
|
|
4423
|
+
return existing && existing.trim().length > 0 ? `${existing.trim()} ${option}` : option;
|
|
4424
|
+
}
|
|
4425
|
+
function supportsPmInheritedListener(record, runtime2) {
|
|
4426
|
+
return (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "inherit" && record.type === "file" && runtime2 === "node" && !isTypescriptFile(record.file);
|
|
4427
|
+
}
|
|
3982
4428
|
function inferRuntimeFromFile(filePath) {
|
|
3983
4429
|
if (isTypescriptFile(filePath) && commandExists("bun")) {
|
|
3984
4430
|
return "bun";
|
|
3985
4431
|
}
|
|
3986
4432
|
return "node";
|
|
3987
4433
|
}
|
|
4434
|
+
function sampleWindowsPmProcessMetrics(pid) {
|
|
4435
|
+
const script = [
|
|
4436
|
+
'$ErrorActionPreference = "Stop"',
|
|
4437
|
+
`$sample = Get-CimInstance -ClassName Win32_PerfFormattedData_PerfProc_Process -Filter "IDProcess = ${pid}" | Select-Object -First 1`,
|
|
4438
|
+
"if (-not $sample) { exit 2 }",
|
|
4439
|
+
"$cpu = [double]$sample.PercentProcessorTime",
|
|
4440
|
+
`$memory = if ($sample.PSObject.Properties.Match('WorkingSetPrivate').Count -gt 0) { [int64]$sample.WorkingSetPrivate } else { [int64](Get-Process -Id ${pid} -ErrorAction Stop).WorkingSet64 }`,
|
|
4441
|
+
'Write-Output ($cpu.ToString([System.Globalization.CultureInfo]::InvariantCulture) + "," + $memory)'
|
|
4442
|
+
].join("; ");
|
|
4443
|
+
const result = (0, import_node_child_process.spawnSync)(
|
|
4444
|
+
"powershell.exe",
|
|
4445
|
+
["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
|
|
4446
|
+
{
|
|
4447
|
+
encoding: "utf8",
|
|
4448
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4449
|
+
windowsHide: true
|
|
4450
|
+
}
|
|
4451
|
+
);
|
|
4452
|
+
if (result.error || result.status !== 0) {
|
|
4453
|
+
return {};
|
|
4454
|
+
}
|
|
4455
|
+
const [cpuText, memoryText] = result.stdout.trim().split(",");
|
|
4456
|
+
const cpuPercent = Number.parseFloat((cpuText ?? "").replace(",", "."));
|
|
4457
|
+
const memoryRssBytes = Number.parseInt(memoryText ?? "", 10);
|
|
4458
|
+
return {
|
|
4459
|
+
cpuPercent: Number.isFinite(cpuPercent) ? cpuPercent : void 0,
|
|
4460
|
+
memoryRssBytes: Number.isFinite(memoryRssBytes) ? memoryRssBytes : void 0
|
|
4461
|
+
};
|
|
4462
|
+
}
|
|
4463
|
+
function samplePosixPmProcessMetrics(pid) {
|
|
4464
|
+
const result = (0, import_node_child_process.spawnSync)(
|
|
4465
|
+
"ps",
|
|
4466
|
+
["-p", String(pid), "-o", "%cpu=", "-o", "rss="],
|
|
4467
|
+
{
|
|
4468
|
+
encoding: "utf8",
|
|
4469
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4470
|
+
windowsHide: true
|
|
4471
|
+
}
|
|
4472
|
+
);
|
|
4473
|
+
if (result.error || result.status !== 0) {
|
|
4474
|
+
return {};
|
|
4475
|
+
}
|
|
4476
|
+
const [cpuText, memoryText] = result.stdout.trim().split(/\s+/, 2);
|
|
4477
|
+
const cpuPercent = Number.parseFloat((cpuText ?? "").replace(",", "."));
|
|
4478
|
+
const rssKilobytes = Number.parseInt(memoryText ?? "", 10);
|
|
4479
|
+
return {
|
|
4480
|
+
cpuPercent: Number.isFinite(cpuPercent) ? cpuPercent : void 0,
|
|
4481
|
+
memoryRssBytes: Number.isFinite(rssKilobytes) ? rssKilobytes * 1024 : void 0
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
function samplePmProcessMetrics(pid) {
|
|
4485
|
+
return process.platform === "win32" ? sampleWindowsPmProcessMetrics(pid) : samplePosixPmProcessMetrics(pid);
|
|
4486
|
+
}
|
|
3988
4487
|
function isPmOnlineWapkRecord(record) {
|
|
3989
4488
|
return record.type === "wapk" && isPmWapkOnlineRunConfig(record.wapkRun);
|
|
3990
4489
|
}
|
|
3991
4490
|
function buildPmCommand(record) {
|
|
4491
|
+
if ((record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "inherit" && record.type !== "file") {
|
|
4492
|
+
throw new Error('pm proxy strategy "inherit" currently supports only Node .js/.mjs/.cjs file targets.');
|
|
4493
|
+
}
|
|
3992
4494
|
if (record.type === "script") {
|
|
3993
4495
|
return {
|
|
3994
4496
|
command: record.script,
|
|
@@ -4060,9 +4562,23 @@ error: ${text}`);
|
|
|
4060
4562
|
}
|
|
4061
4563
|
const executable = preferCurrentExecutable("node");
|
|
4062
4564
|
ensureCommandAvailable(executable, "Node.js runtime");
|
|
4565
|
+
const wantsInheritedListener = (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "inherit";
|
|
4566
|
+
if (wantsInheritedListener && !supportsPmInheritedListener(record, runtime2)) {
|
|
4567
|
+
throw new Error('pm proxy strategy "inherit" currently supports only Node .js/.mjs/.cjs file targets.');
|
|
4568
|
+
}
|
|
4569
|
+
const bootstrapPath = wantsInheritedListener ? resolvePmNodeSharedListenerBootstrapPath() : void 0;
|
|
4570
|
+
if (wantsInheritedListener && !bootstrapPath) {
|
|
4571
|
+
throw new Error("Unable to resolve the PM shared-listener bootstrap module.");
|
|
4572
|
+
}
|
|
4063
4573
|
return {
|
|
4064
4574
|
command: executable,
|
|
4065
4575
|
args: [record.file],
|
|
4576
|
+
env: bootstrapPath ? {
|
|
4577
|
+
NODE_OPTIONS: appendNodeOption(process.env.NODE_OPTIONS, `--require=${bootstrapPath}`),
|
|
4578
|
+
ELIT_PM_LISTEN_MODE: "ipc",
|
|
4579
|
+
ELIT_PM_PUBLIC_PORT: String(record.proxy?.port ?? record.env.PORT ?? "")
|
|
4580
|
+
} : void 0,
|
|
4581
|
+
ipc: Boolean(bootstrapPath),
|
|
4066
4582
|
runtime: runtime2,
|
|
4067
4583
|
preview: `${(0, import_node_path5.basename)(executable)} ${quoteCommandSegment(record.file)}`
|
|
4068
4584
|
};
|
|
@@ -4074,20 +4590,33 @@ error: ${text}`);
|
|
|
4074
4590
|
return {
|
|
4075
4591
|
id,
|
|
4076
4592
|
name: definition.name,
|
|
4593
|
+
baseName: definition.baseName,
|
|
4594
|
+
instanceIndex: definition.instanceIndex,
|
|
4595
|
+
instances: definition.instances,
|
|
4077
4596
|
type: definition.type,
|
|
4078
4597
|
source: definition.source,
|
|
4079
4598
|
cwd: definition.cwd,
|
|
4080
4599
|
runtime: definition.runtime,
|
|
4081
4600
|
env: definition.env,
|
|
4601
|
+
proxy: definition.proxy,
|
|
4082
4602
|
script: definition.script,
|
|
4083
4603
|
file: definition.file,
|
|
4084
4604
|
wapk: definition.wapk,
|
|
4085
4605
|
wapkRun: definition.wapkRun,
|
|
4086
4606
|
autorestart: definition.autorestart,
|
|
4087
4607
|
restartDelay: definition.restartDelay,
|
|
4608
|
+
killTimeout: definition.killTimeout,
|
|
4088
4609
|
maxRestarts: definition.maxRestarts,
|
|
4089
4610
|
password: definition.password,
|
|
4090
4611
|
restartPolicy: definition.restartPolicy,
|
|
4612
|
+
maxMemoryBytes: definition.maxMemoryBytes,
|
|
4613
|
+
memoryAction: definition.memoryAction,
|
|
4614
|
+
cronRestart: definition.cronRestart,
|
|
4615
|
+
expBackoffRestartDelay: definition.expBackoffRestartDelay,
|
|
4616
|
+
expBackoffRestartMaxDelay: definition.expBackoffRestartMaxDelay,
|
|
4617
|
+
restartWindow: definition.restartWindow,
|
|
4618
|
+
waitReady: definition.waitReady,
|
|
4619
|
+
listenTimeout: definition.listenTimeout,
|
|
4091
4620
|
minUptime: definition.minUptime,
|
|
4092
4621
|
watch: definition.watch,
|
|
4093
4622
|
watchPaths: definition.watchPaths,
|
|
@@ -4103,16 +4632,20 @@ error: ${text}`);
|
|
|
4103
4632
|
stoppedAt: void 0,
|
|
4104
4633
|
runnerPid: void 0,
|
|
4105
4634
|
childPid: void 0,
|
|
4635
|
+
proxyTargetPort: void 0,
|
|
4106
4636
|
restartCount: existing?.restartCount ?? 0,
|
|
4637
|
+
reloadRequestedAt: void 0,
|
|
4638
|
+
lastRestartAt: existing?.lastRestartAt,
|
|
4107
4639
|
lastExitCode: existing?.lastExitCode,
|
|
4108
4640
|
error: void 0,
|
|
4641
|
+
proxyReadyAt: void 0,
|
|
4109
4642
|
logFiles: existing?.logFiles ?? {
|
|
4110
4643
|
out: (0, import_node_path5.join)(paths.logsDir, `${id}.out.log`),
|
|
4111
4644
|
err: (0, import_node_path5.join)(paths.logsDir, `${id}.err.log`)
|
|
4112
4645
|
}
|
|
4113
4646
|
};
|
|
4114
4647
|
}
|
|
4115
|
-
function terminateProcessTree(pid) {
|
|
4648
|
+
function terminateProcessTree(pid, options) {
|
|
4116
4649
|
if (process.platform === "win32") {
|
|
4117
4650
|
const result = (0, import_node_child_process.spawnSync)("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
4118
4651
|
stdio: "ignore",
|
|
@@ -4124,7 +4657,17 @@ error: ${text}`);
|
|
|
4124
4657
|
return;
|
|
4125
4658
|
}
|
|
4126
4659
|
try {
|
|
4127
|
-
process.kill(pid, "SIGTERM");
|
|
4660
|
+
process.kill(pid, options?.force ? "SIGKILL" : "SIGTERM");
|
|
4661
|
+
} catch (error) {
|
|
4662
|
+
const code = error.code;
|
|
4663
|
+
if (code !== "ESRCH") {
|
|
4664
|
+
throw error;
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
function sendPmSignal(pid, signal) {
|
|
4669
|
+
try {
|
|
4670
|
+
process.kill(pid, signal);
|
|
4128
4671
|
} catch (error) {
|
|
4129
4672
|
const code = error.code;
|
|
4130
4673
|
if (code !== "ESRCH") {
|
|
@@ -4176,6 +4719,7 @@ error: ${text}`);
|
|
|
4176
4719
|
|
|
4177
4720
|
// src/cli/pm/runner.ts
|
|
4178
4721
|
var import_node_child_process2 = __require("child_process");
|
|
4722
|
+
var import_node_http2 = __require("http");
|
|
4179
4723
|
var import_node_fs4 = __require("fs");
|
|
4180
4724
|
var import_node_os = __require("os");
|
|
4181
4725
|
var import_node_path6 = __require("path");
|
|
@@ -4409,13 +4953,13 @@ error: ${text}`);
|
|
|
4409
4953
|
function join5(...paths) {
|
|
4410
4954
|
return joinPaths(paths, isWindows);
|
|
4411
4955
|
}
|
|
4412
|
-
function
|
|
4956
|
+
function resolve5(...paths) {
|
|
4413
4957
|
return resolvePaths(paths, isWindows);
|
|
4414
4958
|
}
|
|
4415
4959
|
function relative(from, to) {
|
|
4416
4960
|
return relativePath(from, to, isWindows);
|
|
4417
4961
|
}
|
|
4418
|
-
function
|
|
4962
|
+
function dirname3(path) {
|
|
4419
4963
|
return getDirname(path, isWindows);
|
|
4420
4964
|
}
|
|
4421
4965
|
|
|
@@ -4453,14 +4997,14 @@ error: ${text}`);
|
|
|
4453
4997
|
}
|
|
4454
4998
|
if (normalizedPattern && existsSync4(normalizedPattern)) {
|
|
4455
4999
|
try {
|
|
4456
|
-
return statSync2(normalizedPattern).isDirectory() ? normalizedPattern : normalizePath2(
|
|
5000
|
+
return statSync2(normalizedPattern).isDirectory() ? normalizedPattern : normalizePath2(dirname3(normalizedPattern)) || ".";
|
|
4457
5001
|
} catch {
|
|
4458
|
-
return normalizePath2(
|
|
5002
|
+
return normalizePath2(dirname3(normalizedPattern)) || ".";
|
|
4459
5003
|
}
|
|
4460
5004
|
}
|
|
4461
5005
|
const lastSegment = parts[parts.length - 1] || "";
|
|
4462
5006
|
if (lastSegment.includes(".") && !lastSegment.startsWith(".")) {
|
|
4463
|
-
return normalizePath2(
|
|
5007
|
+
return normalizePath2(dirname3(normalizedPattern)) || ".";
|
|
4464
5008
|
}
|
|
4465
5009
|
return normalizedPattern || ".";
|
|
4466
5010
|
}
|
|
@@ -4655,84 +5199,877 @@ error: ${text}`);
|
|
|
4655
5199
|
return watcher;
|
|
4656
5200
|
}
|
|
4657
5201
|
|
|
4658
|
-
// src/cli/pm/
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
5202
|
+
// src/cli/pm/proxy.ts
|
|
5203
|
+
var import_node_http = __require("http");
|
|
5204
|
+
var import_node_https = __require("https");
|
|
5205
|
+
var import_node_net = __require("net");
|
|
5206
|
+
var import_promises = __require("dns/promises");
|
|
5207
|
+
var BLOCKED_IPV4_PREFIXES = [
|
|
5208
|
+
"0.",
|
|
5209
|
+
"10.",
|
|
5210
|
+
"100.64.",
|
|
5211
|
+
"100.65.",
|
|
5212
|
+
"100.66.",
|
|
5213
|
+
"100.67.",
|
|
5214
|
+
"100.68.",
|
|
5215
|
+
"100.69.",
|
|
5216
|
+
"100.70.",
|
|
5217
|
+
"100.71.",
|
|
5218
|
+
"100.72.",
|
|
5219
|
+
"100.73.",
|
|
5220
|
+
"100.74.",
|
|
5221
|
+
"100.75.",
|
|
5222
|
+
"100.76.",
|
|
5223
|
+
"100.77.",
|
|
5224
|
+
"100.78.",
|
|
5225
|
+
"100.79.",
|
|
5226
|
+
"100.80.",
|
|
5227
|
+
"100.81.",
|
|
5228
|
+
"100.82.",
|
|
5229
|
+
"100.83.",
|
|
5230
|
+
"100.84.",
|
|
5231
|
+
"100.85.",
|
|
5232
|
+
"100.86.",
|
|
5233
|
+
"100.87.",
|
|
5234
|
+
"100.88.",
|
|
5235
|
+
"100.89.",
|
|
5236
|
+
"100.90.",
|
|
5237
|
+
"100.91.",
|
|
5238
|
+
"100.92.",
|
|
5239
|
+
"100.93.",
|
|
5240
|
+
"100.94.",
|
|
5241
|
+
"100.95.",
|
|
5242
|
+
"100.96.",
|
|
5243
|
+
"100.97.",
|
|
5244
|
+
"100.98.",
|
|
5245
|
+
"100.99.",
|
|
5246
|
+
"100.100.",
|
|
5247
|
+
"100.101.",
|
|
5248
|
+
"100.102.",
|
|
5249
|
+
"100.103.",
|
|
5250
|
+
"100.104.",
|
|
5251
|
+
"100.105.",
|
|
5252
|
+
"100.106.",
|
|
5253
|
+
"100.107.",
|
|
5254
|
+
"100.108.",
|
|
5255
|
+
"100.109.",
|
|
5256
|
+
"100.110.",
|
|
5257
|
+
"100.111.",
|
|
5258
|
+
"100.112.",
|
|
5259
|
+
"100.113.",
|
|
5260
|
+
"100.114.",
|
|
5261
|
+
"100.115.",
|
|
5262
|
+
"100.116.",
|
|
5263
|
+
"100.117.",
|
|
5264
|
+
"100.118.",
|
|
5265
|
+
"100.119.",
|
|
5266
|
+
"100.120.",
|
|
5267
|
+
"100.121.",
|
|
5268
|
+
"100.122.",
|
|
5269
|
+
"100.123.",
|
|
5270
|
+
"100.124.",
|
|
5271
|
+
"100.125.",
|
|
5272
|
+
"100.126.",
|
|
5273
|
+
"100.127.",
|
|
5274
|
+
"127.",
|
|
5275
|
+
"169.254.",
|
|
5276
|
+
"172.16.",
|
|
5277
|
+
"172.17.",
|
|
5278
|
+
"172.18.",
|
|
5279
|
+
"172.19.",
|
|
5280
|
+
"172.20.",
|
|
5281
|
+
"172.21.",
|
|
5282
|
+
"172.22.",
|
|
5283
|
+
"172.23.",
|
|
5284
|
+
"172.24.",
|
|
5285
|
+
"172.25.",
|
|
5286
|
+
"172.26.",
|
|
5287
|
+
"172.27.",
|
|
5288
|
+
"172.28.",
|
|
5289
|
+
"172.29.",
|
|
5290
|
+
"172.30.",
|
|
5291
|
+
"172.31.",
|
|
5292
|
+
"192.0.2.",
|
|
5293
|
+
"192.88.99.",
|
|
5294
|
+
"192.168.",
|
|
5295
|
+
"198.18.",
|
|
5296
|
+
"198.19.",
|
|
5297
|
+
"198.51.100.",
|
|
5298
|
+
"203.0.113.",
|
|
5299
|
+
"224.",
|
|
5300
|
+
"225.",
|
|
5301
|
+
"226.",
|
|
5302
|
+
"227.",
|
|
5303
|
+
"228.",
|
|
5304
|
+
"229.",
|
|
5305
|
+
"230.",
|
|
5306
|
+
"231.",
|
|
5307
|
+
"232.",
|
|
5308
|
+
"233.",
|
|
5309
|
+
"234.",
|
|
5310
|
+
"235.",
|
|
5311
|
+
"236.",
|
|
5312
|
+
"237.",
|
|
5313
|
+
"238.",
|
|
5314
|
+
"239.",
|
|
5315
|
+
"240.",
|
|
5316
|
+
"241.",
|
|
5317
|
+
"242.",
|
|
5318
|
+
"243.",
|
|
5319
|
+
"244.",
|
|
5320
|
+
"245.",
|
|
5321
|
+
"246.",
|
|
5322
|
+
"247.",
|
|
5323
|
+
"248.",
|
|
5324
|
+
"249.",
|
|
5325
|
+
"250.",
|
|
5326
|
+
"251.",
|
|
5327
|
+
"252.",
|
|
5328
|
+
"253.",
|
|
5329
|
+
"254.",
|
|
5330
|
+
"255."
|
|
5331
|
+
];
|
|
5332
|
+
var ALLOWED_PROXY_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
5333
|
+
function isBlockedIpv4(hostname) {
|
|
5334
|
+
const octets = hostname.split(".");
|
|
5335
|
+
if (octets.length !== 4) return false;
|
|
5336
|
+
const joined = hostname;
|
|
5337
|
+
return BLOCKED_IPV4_PREFIXES.some((prefix) => joined.startsWith(prefix));
|
|
5338
|
+
}
|
|
5339
|
+
function isBlockedIpv6(hostname) {
|
|
5340
|
+
const lower = hostname.toLowerCase();
|
|
5341
|
+
if (lower === "::1" || lower === "::" || lower === "0:0:0:0:0:0:0:1" || lower === "0:0:0:0:0:0:0:0") {
|
|
5342
|
+
return true;
|
|
4665
5343
|
}
|
|
4666
|
-
|
|
4667
|
-
|
|
5344
|
+
const ffffMatch = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
|
|
5345
|
+
if (ffffMatch) {
|
|
5346
|
+
return isBlockedIpv4(ffffMatch[1]);
|
|
4668
5347
|
}
|
|
4669
|
-
|
|
5348
|
+
const compatMatch = lower.match(/^::(\d+\.\d+\.\d+\.\d+)$/);
|
|
5349
|
+
if (compatMatch) {
|
|
5350
|
+
return isBlockedIpv4(compatMatch[1]);
|
|
5351
|
+
}
|
|
5352
|
+
return false;
|
|
4670
5353
|
}
|
|
4671
|
-
|
|
4672
|
-
|
|
5354
|
+
function isSafeHostname(hostname) {
|
|
5355
|
+
if (!hostname) return false;
|
|
5356
|
+
if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) return !isBlockedIpv4(hostname);
|
|
5357
|
+
if (/^\[.*\]$/.test(hostname)) return !isBlockedIpv6(hostname.slice(1, -1));
|
|
5358
|
+
if (hostname.includes(":")) return !isBlockedIpv6(hostname);
|
|
5359
|
+
return true;
|
|
4673
5360
|
}
|
|
4674
|
-
async function
|
|
4675
|
-
|
|
4676
|
-
|
|
5361
|
+
async function safeResolveHostname(hostname) {
|
|
5362
|
+
try {
|
|
5363
|
+
const result = await (0, import_promises.lookup)(hostname);
|
|
5364
|
+
const ip = result.address;
|
|
5365
|
+
if (isBlockedIpv4(ip) || isBlockedIpv6(ip)) {
|
|
5366
|
+
throw new Error(`PM proxy target resolved to a blocked address: ${ip}`);
|
|
5367
|
+
}
|
|
5368
|
+
return ip;
|
|
5369
|
+
} catch (error) {
|
|
5370
|
+
if (error instanceof Error && error.message.includes("blocked address")) {
|
|
5371
|
+
throw error;
|
|
5372
|
+
}
|
|
5373
|
+
throw new Error(`PM proxy failed to resolve target hostname "${hostname}": ${error instanceof Error ? error.message : String(error)}`);
|
|
4677
5374
|
}
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
5375
|
+
}
|
|
5376
|
+
async function validateProxyTargetUrl(target) {
|
|
5377
|
+
if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
|
|
5378
|
+
throw new Error(`PM proxy target protocol "${target.protocol}" is not allowed. Only http: and https: are permitted.`);
|
|
5379
|
+
}
|
|
5380
|
+
const hostname = target.hostname;
|
|
5381
|
+
if (!isSafeHostname(hostname)) {
|
|
5382
|
+
throw new Error(`PM proxy target "${hostname}" resolves to a blocked address and is not allowed.`);
|
|
5383
|
+
}
|
|
5384
|
+
if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) && !/^\[.*\]$/.test(hostname)) {
|
|
5385
|
+
await safeResolveHostname(hostname);
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
function sanitizeProxyRequestPath(requestUrl) {
|
|
5389
|
+
if (!requestUrl || requestUrl === "/") return "/";
|
|
5390
|
+
try {
|
|
5391
|
+
const normalizedInput = requestUrl.replace(/\\/g, "/");
|
|
5392
|
+
const parsed = new URL(normalizedInput, "http://placeholder");
|
|
5393
|
+
if (parsed.username || parsed.password || parsed.hostname !== "placeholder" || parsed.port) {
|
|
5394
|
+
return "/";
|
|
4682
5395
|
}
|
|
4683
|
-
|
|
5396
|
+
const pathname = parsed.pathname || "/";
|
|
5397
|
+
let decodedPathname = pathname;
|
|
5398
|
+
try {
|
|
5399
|
+
decodedPathname = decodeURIComponent(pathname);
|
|
5400
|
+
} catch {
|
|
5401
|
+
return "/";
|
|
5402
|
+
}
|
|
5403
|
+
const lowerPath = pathname.toLowerCase();
|
|
5404
|
+
if (lowerPath.includes("%2f") || lowerPath.includes("%5c") || lowerPath.includes("%40") || lowerPath.includes("%00")) {
|
|
5405
|
+
return "/";
|
|
5406
|
+
}
|
|
5407
|
+
const segments = decodedPathname.split("/");
|
|
5408
|
+
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
5409
|
+
return "/";
|
|
5410
|
+
}
|
|
5411
|
+
const sanitized = pathname + parsed.search;
|
|
5412
|
+
return sanitized.startsWith("/") ? sanitized || "/" : `/${sanitized}`;
|
|
5413
|
+
} catch {
|
|
5414
|
+
return "/";
|
|
4684
5415
|
}
|
|
4685
|
-
return !isProcessAlive(pid);
|
|
4686
5416
|
}
|
|
4687
|
-
|
|
4688
|
-
return
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
5417
|
+
function resolvePmProxyHost(proxy) {
|
|
5418
|
+
return proxy.host?.trim() || "0.0.0.0";
|
|
5419
|
+
}
|
|
5420
|
+
function resolvePmProxyTargetHost(proxy) {
|
|
5421
|
+
return proxy.targetHost?.trim() || "127.0.0.1";
|
|
5422
|
+
}
|
|
5423
|
+
function resolvePmProxyEnvVar(proxy) {
|
|
5424
|
+
return proxy.envVar?.trim() || "PORT";
|
|
5425
|
+
}
|
|
5426
|
+
function buildPmProxyTargetUrl(proxy, targetPort) {
|
|
5427
|
+
return `http://${resolvePmProxyTargetHost(proxy)}:${targetPort}`;
|
|
5428
|
+
}
|
|
5429
|
+
function rewritePmProxyHealthCheckUrl(url, targetHost, targetPort) {
|
|
5430
|
+
const targetUrl = new URL(url);
|
|
5431
|
+
targetUrl.hostname = targetHost;
|
|
5432
|
+
targetUrl.port = String(targetPort);
|
|
5433
|
+
return targetUrl.toString();
|
|
5434
|
+
}
|
|
5435
|
+
async function allocatePmProxyTargetPort(host = "127.0.0.1") {
|
|
5436
|
+
const server = (0, import_node_net.createServer)();
|
|
5437
|
+
return await new Promise((resolve7, reject) => {
|
|
5438
|
+
server.once("error", reject);
|
|
5439
|
+
server.listen(0, host, () => {
|
|
5440
|
+
const address = server.address();
|
|
5441
|
+
if (!address || typeof address === "string") {
|
|
5442
|
+
server.close(() => reject(new Error("Failed to allocate an internal PM proxy port.")));
|
|
4699
5443
|
return;
|
|
4700
5444
|
}
|
|
4701
|
-
|
|
4702
|
-
|
|
5445
|
+
const port = address.port;
|
|
5446
|
+
server.close((error) => {
|
|
5447
|
+
if (error) {
|
|
5448
|
+
reject(error);
|
|
5449
|
+
return;
|
|
5450
|
+
}
|
|
5451
|
+
resolve7(port);
|
|
5452
|
+
});
|
|
4703
5453
|
});
|
|
4704
5454
|
});
|
|
4705
5455
|
}
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
5456
|
+
function buildPmProxyHeaders(headersInput, host) {
|
|
5457
|
+
const headers = {};
|
|
5458
|
+
for (const [key, value] of Object.entries(headersInput)) {
|
|
5459
|
+
if (value !== void 0) {
|
|
5460
|
+
headers[key] = value;
|
|
5461
|
+
}
|
|
5462
|
+
}
|
|
5463
|
+
headers.host = host;
|
|
5464
|
+
return headers;
|
|
5465
|
+
}
|
|
5466
|
+
function writeRawHttpResponse(socket, statusCode, statusMessage, headers) {
|
|
5467
|
+
const lines = [`HTTP/1.1 ${statusCode} ${statusMessage}`];
|
|
5468
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
5469
|
+
if (value === void 0) {
|
|
5470
|
+
continue;
|
|
5471
|
+
}
|
|
5472
|
+
if (Array.isArray(value)) {
|
|
5473
|
+
for (const item of value) {
|
|
5474
|
+
lines.push(`${key}: ${item}`);
|
|
4710
5475
|
}
|
|
4711
|
-
|
|
5476
|
+
continue;
|
|
5477
|
+
}
|
|
5478
|
+
lines.push(`${key}: ${value}`);
|
|
4712
5479
|
}
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
5480
|
+
socket.write(`${lines.join("\r\n")}\r
|
|
5481
|
+
\r
|
|
5482
|
+
`);
|
|
5483
|
+
}
|
|
5484
|
+
async function createPmProxyController(proxy) {
|
|
5485
|
+
let targets = [];
|
|
5486
|
+
let nextTargetIndex = 0;
|
|
5487
|
+
const setResolvedTargets = (nextTargets) => {
|
|
5488
|
+
const unchanged = nextTargets.length === targets.length && nextTargets.every((target, index) => targets[index]?.href === target.href);
|
|
5489
|
+
targets = nextTargets;
|
|
5490
|
+
if (targets.length === 0) {
|
|
5491
|
+
nextTargetIndex = 0;
|
|
4718
5492
|
return;
|
|
4719
5493
|
}
|
|
4720
|
-
if (
|
|
4721
|
-
|
|
5494
|
+
if (!unchanged) {
|
|
5495
|
+
nextTargetIndex = nextTargetIndex % targets.length;
|
|
4722
5496
|
}
|
|
4723
|
-
debounceTimer = setTimeout(() => {
|
|
4724
|
-
debounceTimer = null;
|
|
4725
|
-
onChange(normalizedPath);
|
|
4726
|
-
}, record.watchDebounce);
|
|
4727
|
-
debounceTimer.unref?.();
|
|
4728
5497
|
};
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
5498
|
+
const pickTarget = () => {
|
|
5499
|
+
if (targets.length === 0) {
|
|
5500
|
+
return null;
|
|
5501
|
+
}
|
|
5502
|
+
const target = targets[nextTargetIndex % targets.length];
|
|
5503
|
+
nextTargetIndex = (nextTargetIndex + 1) % targets.length;
|
|
5504
|
+
return target;
|
|
5505
|
+
};
|
|
5506
|
+
const validateTarget = async (target) => {
|
|
5507
|
+
await validateProxyTargetUrl(target);
|
|
5508
|
+
};
|
|
5509
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
5510
|
+
const target = pickTarget();
|
|
5511
|
+
if (!target) {
|
|
5512
|
+
res.statusCode = 503;
|
|
5513
|
+
res.end("PM proxy target is not ready.");
|
|
5514
|
+
return;
|
|
5515
|
+
}
|
|
5516
|
+
const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
|
|
5517
|
+
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5518
|
+
const headers = buildPmProxyHeaders(req.headers, target.host);
|
|
5519
|
+
if (!ALLOWED_PROXY_PROTOCOLS.has(target.protocol)) {
|
|
5520
|
+
res.statusCode = 400;
|
|
5521
|
+
res.end("PM proxy rejected unsafe target protocol.");
|
|
5522
|
+
return;
|
|
5523
|
+
}
|
|
5524
|
+
validateTarget(target).then(() => {
|
|
5525
|
+
const proxyReq = requestLib({
|
|
5526
|
+
protocol: target.protocol,
|
|
5527
|
+
hostname: target.hostname,
|
|
5528
|
+
port: target.port || void 0,
|
|
5529
|
+
path: sanitizedPath,
|
|
5530
|
+
method: req.method,
|
|
5531
|
+
headers
|
|
5532
|
+
}, (proxyRes) => {
|
|
5533
|
+
const outgoingHeaders = {};
|
|
5534
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
5535
|
+
if (value !== void 0) {
|
|
5536
|
+
outgoingHeaders[key] = value;
|
|
5537
|
+
}
|
|
5538
|
+
}
|
|
5539
|
+
res.writeHead(proxyRes.statusCode || 200, outgoingHeaders);
|
|
5540
|
+
proxyRes.pipe(res);
|
|
5541
|
+
});
|
|
5542
|
+
proxyReq.on("error", (error) => {
|
|
5543
|
+
if (!res.headersSent) {
|
|
5544
|
+
res.statusCode = 502;
|
|
5545
|
+
}
|
|
5546
|
+
res.end(`PM proxy error: ${error.message}`);
|
|
5547
|
+
});
|
|
5548
|
+
req.pipe(proxyReq);
|
|
5549
|
+
}).catch((error) => {
|
|
5550
|
+
if (!res.headersSent) {
|
|
5551
|
+
res.statusCode = 403;
|
|
5552
|
+
}
|
|
5553
|
+
res.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
|
|
5554
|
+
});
|
|
5555
|
+
});
|
|
5556
|
+
server.on("upgrade", (req, socket, head) => {
|
|
5557
|
+
const target = pickTarget();
|
|
5558
|
+
if (!target) {
|
|
5559
|
+
writeRawHttpResponse(socket, 503, "Service Unavailable", {
|
|
5560
|
+
connection: "close",
|
|
5561
|
+
"content-length": 0
|
|
5562
|
+
});
|
|
5563
|
+
socket.destroy();
|
|
5564
|
+
return;
|
|
5565
|
+
}
|
|
5566
|
+
const sanitizedPath = sanitizeProxyRequestPath(req.url || "/");
|
|
5567
|
+
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5568
|
+
const targetUrl = new URL(sanitizedPath, target);
|
|
5569
|
+
if (targetUrl.hostname !== target.hostname || targetUrl.port !== target.port || !ALLOWED_PROXY_PROTOCOLS.has(targetUrl.protocol)) {
|
|
5570
|
+
writeRawHttpResponse(socket, 400, "Bad Request", {
|
|
5571
|
+
connection: "close",
|
|
5572
|
+
"content-length": 0
|
|
5573
|
+
});
|
|
5574
|
+
socket.destroy();
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
validateTarget(target).then(() => {
|
|
5578
|
+
const proxyReq = requestLib(targetUrl, {
|
|
5579
|
+
method: req.method,
|
|
5580
|
+
headers: buildPmProxyHeaders(req.headers, target.host)
|
|
5581
|
+
});
|
|
5582
|
+
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
|
|
5583
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 101, proxyRes.statusMessage || "Switching Protocols", proxyRes.headers);
|
|
5584
|
+
if (head.length > 0) {
|
|
5585
|
+
proxySocket.write(head);
|
|
5586
|
+
}
|
|
5587
|
+
if (proxyHead.length > 0) {
|
|
5588
|
+
socket.write(proxyHead);
|
|
5589
|
+
}
|
|
5590
|
+
socket.on("error", () => proxySocket.destroy());
|
|
5591
|
+
proxySocket.on("error", () => socket.destroy());
|
|
5592
|
+
proxySocket.pipe(socket);
|
|
5593
|
+
socket.pipe(proxySocket);
|
|
5594
|
+
});
|
|
5595
|
+
proxyReq.on("response", (proxyRes) => {
|
|
5596
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 502, proxyRes.statusMessage || "Bad Gateway", proxyRes.headers);
|
|
5597
|
+
proxyRes.pipe(socket);
|
|
5598
|
+
});
|
|
5599
|
+
proxyReq.on("error", (error) => {
|
|
5600
|
+
writeRawHttpResponse(socket, 502, "Bad Gateway", {
|
|
5601
|
+
connection: "close",
|
|
5602
|
+
"content-type": "text/plain; charset=utf-8",
|
|
5603
|
+
"content-length": Buffer.byteLength(`PM proxy error: ${error.message}`)
|
|
5604
|
+
});
|
|
5605
|
+
socket.end(`PM proxy error: ${error.message}`);
|
|
5606
|
+
});
|
|
5607
|
+
proxyReq.end();
|
|
5608
|
+
}).catch((error) => {
|
|
5609
|
+
writeRawHttpResponse(socket, 403, "Forbidden", {
|
|
5610
|
+
connection: "close",
|
|
5611
|
+
"content-type": "text/plain; charset=utf-8",
|
|
5612
|
+
"content-length": Buffer.byteLength(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`)
|
|
5613
|
+
});
|
|
5614
|
+
socket.end(`PM proxy blocked target: ${error instanceof Error ? error.message : String(error)}`);
|
|
5615
|
+
});
|
|
5616
|
+
});
|
|
5617
|
+
await new Promise((resolve7, reject) => {
|
|
5618
|
+
server.once("error", reject);
|
|
5619
|
+
server.listen(proxy.port, resolvePmProxyHost(proxy), () => resolve7());
|
|
5620
|
+
});
|
|
5621
|
+
return {
|
|
5622
|
+
setTarget(targetUrl) {
|
|
5623
|
+
if (targetUrl) {
|
|
5624
|
+
const parsed = new URL(targetUrl);
|
|
5625
|
+
validateProxyTargetUrl(parsed).then(() => {
|
|
5626
|
+
setResolvedTargets([parsed]);
|
|
5627
|
+
}).catch((error) => {
|
|
5628
|
+
console.error(`[PM proxy] Blocked setTarget: ${error instanceof Error ? error.message : String(error)}`);
|
|
5629
|
+
setResolvedTargets([]);
|
|
5630
|
+
});
|
|
5631
|
+
} else {
|
|
5632
|
+
setResolvedTargets([]);
|
|
5633
|
+
}
|
|
5634
|
+
},
|
|
5635
|
+
setTargets(targetUrls) {
|
|
5636
|
+
Promise.all(targetUrls.map((url) => validateProxyTargetUrl(new URL(url)))).then(() => {
|
|
5637
|
+
setResolvedTargets(targetUrls.map((targetUrl) => new URL(targetUrl)));
|
|
5638
|
+
}).catch((error) => {
|
|
5639
|
+
console.error(`[PM proxy] Blocked setTargets: ${error instanceof Error ? error.message : String(error)}`);
|
|
5640
|
+
setResolvedTargets([]);
|
|
5641
|
+
});
|
|
5642
|
+
},
|
|
5643
|
+
close() {
|
|
5644
|
+
return new Promise((resolve7, reject) => {
|
|
5645
|
+
server.close((error) => {
|
|
5646
|
+
if (error) {
|
|
5647
|
+
reject(error);
|
|
5648
|
+
return;
|
|
5649
|
+
}
|
|
5650
|
+
resolve7();
|
|
5651
|
+
});
|
|
5652
|
+
});
|
|
5653
|
+
}
|
|
5654
|
+
};
|
|
5655
|
+
}
|
|
5656
|
+
|
|
5657
|
+
// src/cli/pm/runner.ts
|
|
5658
|
+
function writePmLog(stream, message) {
|
|
5659
|
+
if (stream.writableEnded || stream.destroyed) {
|
|
5660
|
+
return;
|
|
5661
|
+
}
|
|
5662
|
+
try {
|
|
5663
|
+
stream.write(`[elit pm] ${(/* @__PURE__ */ new Date()).toISOString()} ${message}${import_node_os.EOL}`);
|
|
5664
|
+
} catch (error) {
|
|
5665
|
+
if (error.code !== "ERR_STREAM_WRITE_AFTER_END") {
|
|
5666
|
+
throw error;
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
function waitForExit(code, signal) {
|
|
5671
|
+
if (typeof code === "number") {
|
|
5672
|
+
return code;
|
|
5673
|
+
}
|
|
5674
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
5675
|
+
return 0;
|
|
5676
|
+
}
|
|
5677
|
+
return 1;
|
|
5678
|
+
}
|
|
5679
|
+
async function delay(milliseconds) {
|
|
5680
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
5681
|
+
}
|
|
5682
|
+
function resolveRunnerPathsFromRecordFile(filePath) {
|
|
5683
|
+
const appsDir = (0, import_node_path6.dirname)(filePath);
|
|
5684
|
+
const dataDir = (0, import_node_path6.dirname)(appsDir);
|
|
5685
|
+
return {
|
|
5686
|
+
dataDir,
|
|
5687
|
+
appsDir,
|
|
5688
|
+
logsDir: (0, import_node_path6.join)(dataDir, "logs"),
|
|
5689
|
+
dumpFile: (0, import_node_path6.join)(dataDir, DEFAULT_PM_DUMP_FILE)
|
|
5690
|
+
};
|
|
5691
|
+
}
|
|
5692
|
+
function usesPmProxyController(record) {
|
|
5693
|
+
return Boolean(record.proxy) && (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "proxy";
|
|
5694
|
+
}
|
|
5695
|
+
function usesPmInheritedListener(record) {
|
|
5696
|
+
return Boolean(record.proxy) && (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "inherit";
|
|
5697
|
+
}
|
|
5698
|
+
function isPmProxyOwner(record) {
|
|
5699
|
+
return usesPmProxyController(record) && record.instanceIndex === 1;
|
|
5700
|
+
}
|
|
5701
|
+
function resolvePmProxyTargetUrls(paths, baseName) {
|
|
5702
|
+
return listPmRecordMatches(paths).filter((match) => match.record.baseName === baseName).filter((match) => usesPmProxyController(match.record)).filter((match) => match.record.desiredState === "running" && Boolean(match.record.proxyReadyAt)).filter((match) => typeof match.record.proxyTargetPort === "number" && match.record.proxyTargetPort > 0).sort((left, right) => left.record.instanceIndex - right.record.instanceIndex).map((match) => buildPmProxyTargetUrl(match.record.proxy, match.record.proxyTargetPort));
|
|
5703
|
+
}
|
|
5704
|
+
async function createPmInheritedListener(proxy) {
|
|
5705
|
+
const server = (0, import_node_http2.createServer)();
|
|
5706
|
+
await new Promise((resolvePromise, reject) => {
|
|
5707
|
+
server.once("error", reject);
|
|
5708
|
+
server.listen(proxy.port, resolvePmProxyHost(proxy), () => resolvePromise());
|
|
5709
|
+
});
|
|
5710
|
+
return server;
|
|
5711
|
+
}
|
|
5712
|
+
function createPmChildIpcState(child, sharedListener) {
|
|
5713
|
+
let bootstrapReady = false;
|
|
5714
|
+
let listenerReady = false;
|
|
5715
|
+
let sharedHandleSent = false;
|
|
5716
|
+
const sendSharedHandle = () => {
|
|
5717
|
+
if (!sharedListener || !bootstrapReady || sharedHandleSent || !child.connected) {
|
|
5718
|
+
return;
|
|
5719
|
+
}
|
|
5720
|
+
sharedHandleSent = true;
|
|
5721
|
+
child.send?.({ type: "elit:pm:listen-handle" }, sharedListener);
|
|
5722
|
+
};
|
|
5723
|
+
const onMessage = (message) => {
|
|
5724
|
+
if (!message || typeof message !== "object") {
|
|
5725
|
+
return;
|
|
5726
|
+
}
|
|
5727
|
+
if (message.type === "elit:pm:bootstrap-ready") {
|
|
5728
|
+
bootstrapReady = true;
|
|
5729
|
+
sendSharedHandle();
|
|
5730
|
+
return;
|
|
5731
|
+
}
|
|
5732
|
+
if (message.type === "elit:pm:listener-ready") {
|
|
5733
|
+
listenerReady = true;
|
|
5734
|
+
}
|
|
5735
|
+
};
|
|
5736
|
+
child.on("message", onMessage);
|
|
5737
|
+
return {
|
|
5738
|
+
get bootstrapReady() {
|
|
5739
|
+
return bootstrapReady;
|
|
5740
|
+
},
|
|
5741
|
+
get listenerReady() {
|
|
5742
|
+
return listenerReady;
|
|
5743
|
+
},
|
|
5744
|
+
stop() {
|
|
5745
|
+
child.off("message", onMessage);
|
|
5746
|
+
}
|
|
5747
|
+
};
|
|
5748
|
+
}
|
|
5749
|
+
function buildPmChildEnv(record, command, targetPort) {
|
|
5750
|
+
return {
|
|
5751
|
+
...process.env,
|
|
5752
|
+
...record.env,
|
|
5753
|
+
...command.env,
|
|
5754
|
+
...usesPmProxyController(record) && targetPort ? {
|
|
5755
|
+
[resolvePmProxyEnvVar(record.proxy)]: String(targetPort),
|
|
5756
|
+
ELIT_PM_PUBLIC_PORT: String(record.proxy.port)
|
|
5757
|
+
} : {},
|
|
5758
|
+
ELIT_PM_NAME: record.name,
|
|
5759
|
+
ELIT_PM_ID: record.id
|
|
5760
|
+
};
|
|
5761
|
+
}
|
|
5762
|
+
function buildPmChildStdio(command, onlineStdinShutdownEnabled) {
|
|
5763
|
+
return [
|
|
5764
|
+
onlineStdinShutdownEnabled ? "pipe" : "ignore",
|
|
5765
|
+
"pipe",
|
|
5766
|
+
"pipe",
|
|
5767
|
+
...command.ipc ? ["ipc"] : []
|
|
5768
|
+
];
|
|
5769
|
+
}
|
|
5770
|
+
function createPmReadinessMonitor(record, onReady, onFailure, options) {
|
|
5771
|
+
if (options?.ipcController) {
|
|
5772
|
+
let stopped2 = false;
|
|
5773
|
+
let timer2 = null;
|
|
5774
|
+
let timeoutTimer2 = null;
|
|
5775
|
+
const clearTimers2 = () => {
|
|
5776
|
+
if (timer2) {
|
|
5777
|
+
clearInterval(timer2);
|
|
5778
|
+
timer2 = null;
|
|
5779
|
+
}
|
|
5780
|
+
if (timeoutTimer2) {
|
|
5781
|
+
clearTimeout(timeoutTimer2);
|
|
5782
|
+
timeoutTimer2 = null;
|
|
5783
|
+
}
|
|
5784
|
+
};
|
|
5785
|
+
const host = record.proxy?.host ?? "0.0.0.0";
|
|
5786
|
+
const port = record.proxy?.port ?? 0;
|
|
5787
|
+
timer2 = setInterval(() => {
|
|
5788
|
+
if (stopped2 || !options.ipcController?.listenerReady) {
|
|
5789
|
+
return;
|
|
5790
|
+
}
|
|
5791
|
+
stopped2 = true;
|
|
5792
|
+
clearTimers2();
|
|
5793
|
+
onReady(`shared listener ready on ${host}:${port}`);
|
|
5794
|
+
}, 25);
|
|
5795
|
+
timer2.unref?.();
|
|
5796
|
+
timeoutTimer2 = setTimeout(() => {
|
|
5797
|
+
if (stopped2) {
|
|
5798
|
+
return;
|
|
5799
|
+
}
|
|
5800
|
+
stopped2 = true;
|
|
5801
|
+
clearTimers2();
|
|
5802
|
+
onFailure(`listen timeout reached after ${record.listenTimeout}ms while waiting for shared listener ${host}:${port}`);
|
|
5803
|
+
}, record.listenTimeout);
|
|
5804
|
+
timeoutTimer2.unref?.();
|
|
5805
|
+
return {
|
|
5806
|
+
stop() {
|
|
5807
|
+
stopped2 = true;
|
|
5808
|
+
clearTimers2();
|
|
5809
|
+
}
|
|
5810
|
+
};
|
|
5811
|
+
}
|
|
5812
|
+
if (!record.waitReady || !record.healthCheck) {
|
|
5813
|
+
return {
|
|
5814
|
+
stop() {
|
|
5815
|
+
}
|
|
5816
|
+
};
|
|
5817
|
+
}
|
|
5818
|
+
const healthCheck = record.healthCheck;
|
|
5819
|
+
const pollInterval = Math.max(50, Math.min(healthCheck.interval, 250));
|
|
5820
|
+
let stopped = false;
|
|
5821
|
+
let timer = null;
|
|
5822
|
+
let timeoutTimer = null;
|
|
5823
|
+
let inFlight = false;
|
|
5824
|
+
const clearTimers = () => {
|
|
5825
|
+
if (timer) {
|
|
5826
|
+
clearInterval(timer);
|
|
5827
|
+
timer = null;
|
|
5828
|
+
}
|
|
5829
|
+
if (timeoutTimer) {
|
|
5830
|
+
clearTimeout(timeoutTimer);
|
|
5831
|
+
timeoutTimer = null;
|
|
5832
|
+
}
|
|
5833
|
+
};
|
|
5834
|
+
const runHealthCheck = async () => {
|
|
5835
|
+
if (stopped || inFlight) {
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
inFlight = true;
|
|
5839
|
+
const controller = new AbortController();
|
|
5840
|
+
const timeoutId = setTimeout(() => controller.abort(), healthCheck.timeout);
|
|
5841
|
+
timeoutId.unref?.();
|
|
5842
|
+
try {
|
|
5843
|
+
const response = await fetch(healthCheck.url, {
|
|
5844
|
+
method: "GET",
|
|
5845
|
+
signal: controller.signal
|
|
5846
|
+
});
|
|
5847
|
+
if (stopped) {
|
|
5848
|
+
return;
|
|
5849
|
+
}
|
|
5850
|
+
if (!response.ok) {
|
|
5851
|
+
throw new Error(`health check returned ${response.status}`);
|
|
5852
|
+
}
|
|
5853
|
+
stopped = true;
|
|
5854
|
+
clearTimers();
|
|
5855
|
+
onReady(`readiness check passed: ${healthCheck.url}`);
|
|
5856
|
+
} catch {
|
|
5857
|
+
} finally {
|
|
5858
|
+
clearTimeout(timeoutId);
|
|
5859
|
+
inFlight = false;
|
|
5860
|
+
}
|
|
5861
|
+
};
|
|
5862
|
+
timeoutTimer = setTimeout(() => {
|
|
5863
|
+
if (stopped) {
|
|
5864
|
+
return;
|
|
5865
|
+
}
|
|
5866
|
+
stopped = true;
|
|
5867
|
+
clearTimers();
|
|
5868
|
+
onFailure(`listen timeout reached after ${record.listenTimeout}ms while waiting for ${healthCheck.url}`);
|
|
5869
|
+
}, record.listenTimeout);
|
|
5870
|
+
timeoutTimer.unref?.();
|
|
5871
|
+
void runHealthCheck();
|
|
5872
|
+
timer = setInterval(() => {
|
|
5873
|
+
void runHealthCheck();
|
|
5874
|
+
}, pollInterval);
|
|
5875
|
+
timer.unref?.();
|
|
5876
|
+
return {
|
|
5877
|
+
stop() {
|
|
5878
|
+
stopped = true;
|
|
5879
|
+
clearTimers();
|
|
5880
|
+
}
|
|
5881
|
+
};
|
|
5882
|
+
}
|
|
5883
|
+
function resolvePmStopTimeout(record) {
|
|
5884
|
+
if (isPmOnlineWapkRecord(record)) {
|
|
5885
|
+
return Math.max(record.killTimeout, PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS);
|
|
5886
|
+
}
|
|
5887
|
+
return record.killTimeout;
|
|
5888
|
+
}
|
|
5889
|
+
function supportsPmProxyReload(record) {
|
|
5890
|
+
return Boolean(record.proxy) && record.instances === 1;
|
|
5891
|
+
}
|
|
5892
|
+
function buildPmMonitorRecord(record, targetPort) {
|
|
5893
|
+
if (!record.proxy || !targetPort) {
|
|
5894
|
+
return record;
|
|
5895
|
+
}
|
|
5896
|
+
const targetHost = resolvePmProxyTargetHost(record.proxy);
|
|
5897
|
+
return {
|
|
5898
|
+
...record,
|
|
5899
|
+
proxyTargetPort: targetPort,
|
|
5900
|
+
healthCheck: record.healthCheck ? {
|
|
5901
|
+
...record.healthCheck,
|
|
5902
|
+
url: rewritePmProxyHealthCheckUrl(record.healthCheck.url, targetHost, targetPort)
|
|
5903
|
+
} : void 0
|
|
5904
|
+
};
|
|
5905
|
+
}
|
|
5906
|
+
async function waitForProcessTermination(pid, timeoutMs) {
|
|
5907
|
+
if (!pid || !isProcessAlive(pid)) {
|
|
5908
|
+
return true;
|
|
5909
|
+
}
|
|
5910
|
+
const deadline = Date.now() + timeoutMs;
|
|
5911
|
+
while (Date.now() < deadline) {
|
|
5912
|
+
if (!isProcessAlive(pid)) {
|
|
5913
|
+
return true;
|
|
5914
|
+
}
|
|
5915
|
+
await delay(DEFAULT_PM_STOP_POLL_MS);
|
|
5916
|
+
}
|
|
5917
|
+
return !isProcessAlive(pid);
|
|
5918
|
+
}
|
|
5919
|
+
async function waitForManagedChildExit(child) {
|
|
5920
|
+
return await new Promise((resolvePromise) => {
|
|
5921
|
+
let resolved = false;
|
|
5922
|
+
child.once("error", (error) => {
|
|
5923
|
+
if (resolved) {
|
|
5924
|
+
return;
|
|
5925
|
+
}
|
|
5926
|
+
resolved = true;
|
|
5927
|
+
resolvePromise({ code: 1, signal: null, error: error instanceof Error ? error.message : String(error) });
|
|
5928
|
+
});
|
|
5929
|
+
child.once("close", (code, signal) => {
|
|
5930
|
+
if (resolved) {
|
|
5931
|
+
return;
|
|
5932
|
+
}
|
|
5933
|
+
resolved = true;
|
|
5934
|
+
resolvePromise({ code, signal });
|
|
5935
|
+
});
|
|
5936
|
+
});
|
|
5937
|
+
}
|
|
5938
|
+
async function createPmChildControllers(record, child, stdoutLog, stderrLog, requestPlannedRestart, onReady, options) {
|
|
5939
|
+
let healthMonitor = {
|
|
5940
|
+
stop() {
|
|
5941
|
+
}
|
|
5942
|
+
};
|
|
5943
|
+
let memoryMonitor = {
|
|
5944
|
+
stop() {
|
|
5945
|
+
}
|
|
5946
|
+
};
|
|
5947
|
+
let scheduleMonitor = {
|
|
5948
|
+
stop() {
|
|
5949
|
+
}
|
|
5950
|
+
};
|
|
5951
|
+
const startHealthMonitor = () => {
|
|
5952
|
+
healthMonitor = createPmHealthMonitor(
|
|
5953
|
+
record,
|
|
5954
|
+
(message) => requestPlannedRestart("health", message),
|
|
5955
|
+
(message) => writePmLog(stdoutLog, message)
|
|
5956
|
+
);
|
|
5957
|
+
};
|
|
5958
|
+
const needsReadySignal = Boolean(options?.ipcController) || record.waitReady;
|
|
5959
|
+
const readinessMonitor = options?.ready || !needsReadySignal ? { stop() {
|
|
5960
|
+
} } : createPmReadinessMonitor(
|
|
5961
|
+
record,
|
|
5962
|
+
(message) => {
|
|
5963
|
+
onReady(message);
|
|
5964
|
+
startHealthMonitor();
|
|
5965
|
+
},
|
|
5966
|
+
(message) => requestPlannedRestart("startup", message),
|
|
5967
|
+
{ ipcController: options?.ipcController }
|
|
5968
|
+
);
|
|
5969
|
+
const watchController = await createPmWatchController(
|
|
5970
|
+
record,
|
|
5971
|
+
(changedPath) => requestPlannedRestart("watch", changedPath),
|
|
5972
|
+
(message) => writePmLog(stderrLog, `watch error: ${message}`)
|
|
5973
|
+
);
|
|
5974
|
+
memoryMonitor = createPmMemoryMonitor(
|
|
5975
|
+
record,
|
|
5976
|
+
child.pid,
|
|
5977
|
+
(kind, message) => requestPlannedRestart(kind, message)
|
|
5978
|
+
);
|
|
5979
|
+
scheduleMonitor = createPmScheduleMonitor(
|
|
5980
|
+
record,
|
|
5981
|
+
(message) => requestPlannedRestart("cron", message),
|
|
5982
|
+
(message) => writePmLog(stdoutLog, message)
|
|
5983
|
+
);
|
|
5984
|
+
if (options?.ready || !needsReadySignal) {
|
|
5985
|
+
startHealthMonitor();
|
|
5986
|
+
}
|
|
5987
|
+
return {
|
|
5988
|
+
async stop() {
|
|
5989
|
+
await watchController.close();
|
|
5990
|
+
readinessMonitor.stop();
|
|
5991
|
+
healthMonitor.stop();
|
|
5992
|
+
memoryMonitor.stop();
|
|
5993
|
+
scheduleMonitor.stop();
|
|
5994
|
+
}
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
async function waitForPmChildReady(record, child, ipcController) {
|
|
5998
|
+
if (!ipcController && (!record.waitReady || !record.healthCheck)) {
|
|
5999
|
+
return { ready: true };
|
|
6000
|
+
}
|
|
6001
|
+
let readyMessage;
|
|
6002
|
+
let failureMessage;
|
|
6003
|
+
let exitResult;
|
|
6004
|
+
const readinessMonitor = createPmReadinessMonitor(
|
|
6005
|
+
record,
|
|
6006
|
+
(message) => {
|
|
6007
|
+
readyMessage = message;
|
|
6008
|
+
},
|
|
6009
|
+
(message) => {
|
|
6010
|
+
failureMessage = message;
|
|
6011
|
+
},
|
|
6012
|
+
{ ipcController }
|
|
6013
|
+
);
|
|
6014
|
+
void waitForManagedChildExit(child).then((result) => {
|
|
6015
|
+
exitResult = result;
|
|
6016
|
+
});
|
|
6017
|
+
while (!readyMessage && !failureMessage && !exitResult) {
|
|
6018
|
+
await delay(25);
|
|
6019
|
+
}
|
|
6020
|
+
readinessMonitor.stop();
|
|
6021
|
+
if (readyMessage) {
|
|
6022
|
+
return { ready: true, message: readyMessage };
|
|
6023
|
+
}
|
|
6024
|
+
return {
|
|
6025
|
+
ready: false,
|
|
6026
|
+
message: failureMessage,
|
|
6027
|
+
exitResult
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
async function stopProxyManagedChild(child, record, stderrLog) {
|
|
6031
|
+
if (!child.pid || !isProcessAlive(child.pid)) {
|
|
6032
|
+
return;
|
|
6033
|
+
}
|
|
6034
|
+
terminateProcessTree(child.pid);
|
|
6035
|
+
const stopTimeout = resolvePmStopTimeout(record);
|
|
6036
|
+
const stopped = await waitForProcessTermination(child.pid, stopTimeout);
|
|
6037
|
+
if (!stopped && child.pid && isProcessAlive(child.pid)) {
|
|
6038
|
+
writePmLog(stderrLog, `proxy handoff shutdown timed out after ${stopTimeout}ms; forcing process termination`);
|
|
6039
|
+
terminateProcessTree(child.pid, { force: true });
|
|
6040
|
+
await waitForProcessTermination(child.pid, DEFAULT_PM_STOP_POLL_MS);
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
async function createPmWatchController(record, onChange, onError) {
|
|
6044
|
+
if (!record.watch || record.watchPaths.length === 0) {
|
|
6045
|
+
return {
|
|
6046
|
+
async close() {
|
|
6047
|
+
}
|
|
6048
|
+
};
|
|
6049
|
+
}
|
|
6050
|
+
const watcher = watch(record.watchPaths);
|
|
6051
|
+
let debounceTimer = null;
|
|
6052
|
+
const scheduleRestart = (filePath) => {
|
|
6053
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
6054
|
+
if (isIgnoredWatchPath(normalizedPath, record.watchIgnore)) {
|
|
6055
|
+
return;
|
|
6056
|
+
}
|
|
6057
|
+
if (debounceTimer) {
|
|
6058
|
+
clearTimeout(debounceTimer);
|
|
6059
|
+
}
|
|
6060
|
+
debounceTimer = setTimeout(() => {
|
|
6061
|
+
debounceTimer = null;
|
|
6062
|
+
onChange(normalizedPath);
|
|
6063
|
+
}, record.watchDebounce);
|
|
6064
|
+
debounceTimer.unref?.();
|
|
6065
|
+
};
|
|
6066
|
+
watcher.on("add", scheduleRestart);
|
|
6067
|
+
watcher.on("change", scheduleRestart);
|
|
6068
|
+
watcher.on("unlink", scheduleRestart);
|
|
6069
|
+
watcher.on("error", (error) => onError(error instanceof Error ? error.message : String(error)));
|
|
6070
|
+
return {
|
|
6071
|
+
async close() {
|
|
6072
|
+
if (debounceTimer) {
|
|
4736
6073
|
clearTimeout(debounceTimer);
|
|
4737
6074
|
debounceTimer = null;
|
|
4738
6075
|
}
|
|
@@ -4766,11 +6103,17 @@ error: ${text}`);
|
|
|
4766
6103
|
method: "GET",
|
|
4767
6104
|
signal: controller.signal
|
|
4768
6105
|
});
|
|
6106
|
+
if (stopped) {
|
|
6107
|
+
return;
|
|
6108
|
+
}
|
|
4769
6109
|
if (!response.ok) {
|
|
4770
6110
|
throw new Error(`health check returned ${response.status}`);
|
|
4771
6111
|
}
|
|
4772
6112
|
failureCount = 0;
|
|
4773
6113
|
} catch (error) {
|
|
6114
|
+
if (stopped) {
|
|
6115
|
+
return;
|
|
6116
|
+
}
|
|
4774
6117
|
failureCount += 1;
|
|
4775
6118
|
const message = error instanceof Error ? error.message : String(error);
|
|
4776
6119
|
onLog(`health check failed (${failureCount}/${healthCheck.maxFailures}): ${message}`);
|
|
@@ -4805,6 +6148,90 @@ error: ${text}`);
|
|
|
4805
6148
|
}
|
|
4806
6149
|
};
|
|
4807
6150
|
}
|
|
6151
|
+
function createPmMemoryMonitor(record, pid, onFailure) {
|
|
6152
|
+
if (!record.maxMemoryBytes || !pid) {
|
|
6153
|
+
return {
|
|
6154
|
+
stop() {
|
|
6155
|
+
}
|
|
6156
|
+
};
|
|
6157
|
+
}
|
|
6158
|
+
const memoryLimit = record.maxMemoryBytes;
|
|
6159
|
+
let stopped = false;
|
|
6160
|
+
const timer = setInterval(() => {
|
|
6161
|
+
if (stopped) {
|
|
6162
|
+
return;
|
|
6163
|
+
}
|
|
6164
|
+
const memoryRssBytes = samplePmProcessMetrics(pid).memoryRssBytes;
|
|
6165
|
+
if (memoryRssBytes === void 0 || memoryRssBytes <= memoryLimit) {
|
|
6166
|
+
return;
|
|
6167
|
+
}
|
|
6168
|
+
stopped = true;
|
|
6169
|
+
onFailure(record.memoryAction === "stop" ? "memory-stop" : "memory", `memory usage ${memoryRssBytes} exceeded limit ${memoryLimit}`);
|
|
6170
|
+
}, DEFAULT_PM_MEMORY_CHECK_INTERVAL);
|
|
6171
|
+
timer.unref?.();
|
|
6172
|
+
return {
|
|
6173
|
+
stop() {
|
|
6174
|
+
stopped = true;
|
|
6175
|
+
clearInterval(timer);
|
|
6176
|
+
}
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
function createPmScheduleMonitor(record, onTrigger, onLog) {
|
|
6180
|
+
if (!record.cronRestart) {
|
|
6181
|
+
return {
|
|
6182
|
+
stop() {
|
|
6183
|
+
}
|
|
6184
|
+
};
|
|
6185
|
+
}
|
|
6186
|
+
const schedule = parsePmRestartSchedule(record.cronRestart, "pm cronRestart");
|
|
6187
|
+
let stopped = false;
|
|
6188
|
+
let timer = null;
|
|
6189
|
+
const armTimer = (from = /* @__PURE__ */ new Date()) => {
|
|
6190
|
+
const nextOccurrence = resolveNextPmScheduleOccurrence(schedule, from);
|
|
6191
|
+
if (!nextOccurrence) {
|
|
6192
|
+
onLog(`schedule has no next occurrence: ${record.cronRestart}`);
|
|
6193
|
+
return;
|
|
6194
|
+
}
|
|
6195
|
+
const delayMs = Math.max(0, nextOccurrence.getTime() - Date.now());
|
|
6196
|
+
timer = setTimeout(() => {
|
|
6197
|
+
timer = null;
|
|
6198
|
+
if (stopped) {
|
|
6199
|
+
return;
|
|
6200
|
+
}
|
|
6201
|
+
onTrigger(`restart schedule matched: ${record.cronRestart}`);
|
|
6202
|
+
}, delayMs);
|
|
6203
|
+
timer.unref?.();
|
|
6204
|
+
};
|
|
6205
|
+
armTimer();
|
|
6206
|
+
return {
|
|
6207
|
+
stop() {
|
|
6208
|
+
stopped = true;
|
|
6209
|
+
if (timer) {
|
|
6210
|
+
clearTimeout(timer);
|
|
6211
|
+
timer = null;
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
};
|
|
6215
|
+
}
|
|
6216
|
+
function resolvePmRestartDelay(record, restartCount, shouldApplyBackoff) {
|
|
6217
|
+
if (!shouldApplyBackoff || !record.expBackoffRestartDelay) {
|
|
6218
|
+
return record.restartDelay;
|
|
6219
|
+
}
|
|
6220
|
+
const exponent = Math.max(0, restartCount - 1);
|
|
6221
|
+
return Math.min(record.expBackoffRestartDelay * 2 ** exponent, record.expBackoffRestartMaxDelay ?? DEFAULT_PM_EXP_BACKOFF_MAX_DELAY);
|
|
6222
|
+
}
|
|
6223
|
+
function resolvePmRestartCountBase(record, wasStable, restartKind) {
|
|
6224
|
+
if (wasStable) {
|
|
6225
|
+
return 0;
|
|
6226
|
+
}
|
|
6227
|
+
if (record.restartWindow && record.lastRestartAt) {
|
|
6228
|
+
const lastRestartTime = Date.parse(record.lastRestartAt);
|
|
6229
|
+
if (!Number.isNaN(lastRestartTime) && Date.now() - lastRestartTime > record.restartWindow) {
|
|
6230
|
+
return 0;
|
|
6231
|
+
}
|
|
6232
|
+
}
|
|
6233
|
+
return restartKind === "watch" ? record.restartCount ?? 0 : record.restartCount ?? 0;
|
|
6234
|
+
}
|
|
4808
6235
|
function readPlannedRestartRequest(state) {
|
|
4809
6236
|
return state.request;
|
|
4810
6237
|
}
|
|
@@ -4818,6 +6245,16 @@ error: ${text}`);
|
|
|
4818
6245
|
(0, import_node_fs4.mkdirSync)((0, import_node_path6.dirname)(initialRecord.logFiles.err), { recursive: true });
|
|
4819
6246
|
const stdoutLog = (0, import_node_fs4.createWriteStream)(initialRecord.logFiles.out, { flags: "a" });
|
|
4820
6247
|
const stderrLog = (0, import_node_fs4.createWriteStream)(initialRecord.logFiles.err, { flags: "a" });
|
|
6248
|
+
let proxyController = null;
|
|
6249
|
+
let proxyTargetSyncTimer = null;
|
|
6250
|
+
let inheritedListener = null;
|
|
6251
|
+
const runnerPaths = resolveRunnerPathsFromRecordFile(filePath);
|
|
6252
|
+
const syncOwnedProxyTargets = (baseName) => {
|
|
6253
|
+
if (!proxyController) {
|
|
6254
|
+
return;
|
|
6255
|
+
}
|
|
6256
|
+
proxyController.setTargets(resolvePmProxyTargetUrls(runnerPaths, baseName));
|
|
6257
|
+
};
|
|
4821
6258
|
const persist = (mutator) => {
|
|
4822
6259
|
const current = readLatestPmRecord(filePath, record);
|
|
4823
6260
|
record = mutator(current);
|
|
@@ -4830,26 +6267,30 @@ error: ${text}`);
|
|
|
4830
6267
|
activeChildStopTimer = null;
|
|
4831
6268
|
}
|
|
4832
6269
|
};
|
|
6270
|
+
const scheduleForcedActiveChildStop = (timeoutMs, reason) => {
|
|
6271
|
+
if (!activeChild?.pid || process.platform === "win32") {
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
clearActiveChildStopTimer();
|
|
6275
|
+
activeChildStopTimer = setTimeout(() => {
|
|
6276
|
+
if (activeChild?.pid && isProcessAlive(activeChild.pid)) {
|
|
6277
|
+
writePmLog(stderrLog, `${reason} after ${timeoutMs}ms; forcing process termination`);
|
|
6278
|
+
terminateProcessTree(activeChild.pid, { force: true });
|
|
6279
|
+
}
|
|
6280
|
+
}, timeoutMs);
|
|
6281
|
+
activeChildStopTimer.unref?.();
|
|
6282
|
+
};
|
|
4833
6283
|
const stopActiveChild = () => {
|
|
4834
6284
|
if (!activeChild?.pid || !isProcessAlive(activeChild.pid)) {
|
|
4835
6285
|
return;
|
|
4836
6286
|
}
|
|
4837
6287
|
const current = readLatestPmRecord(filePath, record);
|
|
6288
|
+
const stopTimeout = resolvePmStopTimeout(current);
|
|
4838
6289
|
if (isPmOnlineWapkRecord(current) && activeChild.stdin && !activeChild.stdin.destroyed && activeChild.stdin.writable) {
|
|
4839
6290
|
try {
|
|
4840
6291
|
activeChild.stdin.end(`${PM_WAPK_ONLINE_SHUTDOWN_COMMAND}
|
|
4841
6292
|
`);
|
|
4842
|
-
|
|
4843
|
-
activeChildStopTimer = setTimeout(() => {
|
|
4844
|
-
if (activeChild?.pid && isProcessAlive(activeChild.pid)) {
|
|
4845
|
-
writePmLog(
|
|
4846
|
-
stderrLog,
|
|
4847
|
-
`graceful WAPK online shutdown timed out after ${PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS}ms; forcing process termination`
|
|
4848
|
-
);
|
|
4849
|
-
terminateProcessTree(activeChild.pid);
|
|
4850
|
-
}
|
|
4851
|
-
}, PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS);
|
|
4852
|
-
activeChildStopTimer.unref?.();
|
|
6293
|
+
scheduleForcedActiveChildStop(stopTimeout, "graceful WAPK online shutdown timed out");
|
|
4853
6294
|
return;
|
|
4854
6295
|
} catch (error) {
|
|
4855
6296
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4857,6 +6298,7 @@ error: ${text}`);
|
|
|
4857
6298
|
}
|
|
4858
6299
|
}
|
|
4859
6300
|
terminateProcessTree(activeChild.pid);
|
|
6301
|
+
scheduleForcedActiveChildStop(stopTimeout, "graceful shutdown timed out");
|
|
4860
6302
|
};
|
|
4861
6303
|
const requestManagedStop = (reason) => {
|
|
4862
6304
|
if (stopRequested) {
|
|
@@ -4894,6 +6336,17 @@ error: ${text}`);
|
|
|
4894
6336
|
let command;
|
|
4895
6337
|
try {
|
|
4896
6338
|
command = buildPmCommand(latest);
|
|
6339
|
+
if (latest.proxy && isPmProxyOwner(latest) && !proxyController) {
|
|
6340
|
+
proxyController = await createPmProxyController(latest.proxy);
|
|
6341
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6342
|
+
if (!proxyTargetSyncTimer) {
|
|
6343
|
+
proxyTargetSyncTimer = setInterval(() => syncOwnedProxyTargets(latest.baseName), 50);
|
|
6344
|
+
proxyTargetSyncTimer.unref?.();
|
|
6345
|
+
}
|
|
6346
|
+
}
|
|
6347
|
+
if (latest.proxy && usesPmInheritedListener(latest) && !inheritedListener) {
|
|
6348
|
+
inheritedListener = await createPmInheritedListener(latest.proxy);
|
|
6349
|
+
}
|
|
4897
6350
|
} catch (error) {
|
|
4898
6351
|
const message = error instanceof Error ? error.message : String(error);
|
|
4899
6352
|
writePmLog(stderrLog, message);
|
|
@@ -4903,25 +6356,24 @@ error: ${text}`);
|
|
|
4903
6356
|
error: message,
|
|
4904
6357
|
runnerPid: void 0,
|
|
4905
6358
|
childPid: void 0,
|
|
6359
|
+
proxyTargetPort: void 0,
|
|
6360
|
+
proxyReadyAt: void 0,
|
|
4906
6361
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4907
6362
|
}));
|
|
4908
6363
|
return;
|
|
4909
6364
|
}
|
|
4910
6365
|
const onlineStdinShutdownEnabled = isPmOnlineWapkRecord(latest);
|
|
4911
|
-
const
|
|
6366
|
+
const initialTargetPort = usesPmProxyController(latest) ? await allocatePmProxyTargetPort(resolvePmProxyTargetHost(latest.proxy)) : void 0;
|
|
6367
|
+
const monitorRecord = buildPmMonitorRecord(latest, initialTargetPort);
|
|
6368
|
+
let child = (0, import_node_child_process2.spawn)(command.command, command.args, {
|
|
4912
6369
|
cwd: latest.cwd,
|
|
4913
|
-
env:
|
|
4914
|
-
|
|
4915
|
-
...latest.env,
|
|
4916
|
-
...command.env,
|
|
4917
|
-
ELIT_PM_NAME: latest.name,
|
|
4918
|
-
ELIT_PM_ID: latest.id
|
|
4919
|
-
},
|
|
4920
|
-
stdio: [onlineStdinShutdownEnabled ? "pipe" : "ignore", "pipe", "pipe"],
|
|
6370
|
+
env: buildPmChildEnv(latest, command, initialTargetPort),
|
|
6371
|
+
stdio: buildPmChildStdio(command, onlineStdinShutdownEnabled),
|
|
4921
6372
|
windowsHide: true,
|
|
4922
6373
|
shell: command.shell
|
|
4923
6374
|
});
|
|
4924
|
-
|
|
6375
|
+
let childStartedAt = Date.now();
|
|
6376
|
+
let childIpcState = command.ipc ? createPmChildIpcState(child, inheritedListener) : void 0;
|
|
4925
6377
|
activeChild = child;
|
|
4926
6378
|
if (child.stdout) {
|
|
4927
6379
|
child.stdout.pipe(stdoutLog, { end: false });
|
|
@@ -4929,26 +6381,38 @@ error: ${text}`);
|
|
|
4929
6381
|
if (child.stderr) {
|
|
4930
6382
|
child.stderr.pipe(stderrLog, { end: false });
|
|
4931
6383
|
}
|
|
6384
|
+
let childWaitState = { settled: false, result: void 0 };
|
|
6385
|
+
void waitForManagedChildExit(child).then((result) => {
|
|
6386
|
+
childWaitState.result = result;
|
|
6387
|
+
childWaitState.settled = true;
|
|
6388
|
+
});
|
|
4932
6389
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6390
|
+
const waitingForReady = latest.waitReady || Boolean(childIpcState);
|
|
4933
6391
|
persist((current2) => ({
|
|
4934
6392
|
...current2,
|
|
4935
|
-
status: "online",
|
|
6393
|
+
status: waitingForReady ? "starting" : "online",
|
|
4936
6394
|
commandPreview: command.preview,
|
|
4937
6395
|
runtime: command.runtime ?? current2.runtime,
|
|
4938
6396
|
runnerPid: process.pid,
|
|
4939
6397
|
childPid: child.pid,
|
|
6398
|
+
proxyTargetPort: initialTargetPort,
|
|
6399
|
+
proxyReadyAt: !waitingForReady && usesPmProxyController(latest) ? startedAt : void 0,
|
|
4940
6400
|
startedAt,
|
|
4941
6401
|
stoppedAt: void 0,
|
|
6402
|
+
reloadRequestedAt: void 0,
|
|
4942
6403
|
error: void 0,
|
|
4943
6404
|
updatedAt: startedAt
|
|
4944
6405
|
}));
|
|
4945
6406
|
writePmLog(stdoutLog, `started ${command.preview}${child.pid ? ` (pid ${child.pid})` : ""}`);
|
|
6407
|
+
if (isPmProxyOwner(latest) && !waitingForReady) {
|
|
6408
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6409
|
+
}
|
|
4946
6410
|
const requestPlannedRestart = (kind, detail) => {
|
|
4947
6411
|
if (stopRequested || restartState.request) {
|
|
4948
6412
|
return;
|
|
4949
6413
|
}
|
|
4950
6414
|
restartState.request = { kind, detail };
|
|
4951
|
-
writePmLog(kind === "
|
|
6415
|
+
writePmLog(kind === "watch" || kind === "cron" ? stdoutLog : stderrLog, `${kind} restart requested: ${detail}`);
|
|
4952
6416
|
persist((current2) => ({
|
|
4953
6417
|
...current2,
|
|
4954
6418
|
status: "restarting",
|
|
@@ -4956,27 +6420,124 @@ error: ${text}`);
|
|
|
4956
6420
|
}));
|
|
4957
6421
|
stopActiveChild();
|
|
4958
6422
|
};
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
6423
|
+
let controllers = await createPmChildControllers(
|
|
6424
|
+
monitorRecord,
|
|
6425
|
+
child,
|
|
6426
|
+
stdoutLog,
|
|
6427
|
+
stderrLog,
|
|
6428
|
+
requestPlannedRestart,
|
|
6429
|
+
(message) => {
|
|
6430
|
+
const readyAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6431
|
+
if (isPmProxyOwner(latest)) {
|
|
6432
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6433
|
+
}
|
|
6434
|
+
persist((current2) => ({
|
|
6435
|
+
...current2,
|
|
6436
|
+
status: "online",
|
|
6437
|
+
proxyTargetPort: initialTargetPort,
|
|
6438
|
+
proxyReadyAt: readyAt,
|
|
6439
|
+
updatedAt: readyAt
|
|
6440
|
+
}));
|
|
6441
|
+
writePmLog(stdoutLog, message);
|
|
6442
|
+
},
|
|
6443
|
+
{ ready: !waitingForReady, ipcController: childIpcState }
|
|
4968
6444
|
);
|
|
4969
|
-
|
|
6445
|
+
let handledReloadAt = latest.reloadRequestedAt;
|
|
6446
|
+
while (!childWaitState.settled) {
|
|
4970
6447
|
const latestRecord = readLatestPmRecord(filePath, record);
|
|
4971
|
-
if (
|
|
6448
|
+
if (latestRecord.desiredState === "stopped" && !stopRequested) {
|
|
4972
6449
|
requestManagedStop("stop requested by PM control state");
|
|
4973
6450
|
}
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
6451
|
+
const reloadRequestedAt = latestRecord.reloadRequestedAt;
|
|
6452
|
+
if (!stopRequested && supportsPmProxyReload(latestRecord) && reloadRequestedAt && reloadRequestedAt !== handledReloadAt && latestRecord.proxy) {
|
|
6453
|
+
handledReloadAt = reloadRequestedAt;
|
|
6454
|
+
const replacementTargetPort = usesPmProxyController(latestRecord) ? await allocatePmProxyTargetPort(resolvePmProxyTargetHost(latestRecord.proxy)) : void 0;
|
|
6455
|
+
const replacementMonitorRecord = buildPmMonitorRecord(latestRecord, replacementTargetPort);
|
|
6456
|
+
const replacementChild = (0, import_node_child_process2.spawn)(command.command, command.args, {
|
|
6457
|
+
cwd: latestRecord.cwd,
|
|
6458
|
+
env: buildPmChildEnv(latestRecord, command, replacementTargetPort),
|
|
6459
|
+
stdio: buildPmChildStdio(command, onlineStdinShutdownEnabled),
|
|
6460
|
+
windowsHide: true,
|
|
6461
|
+
shell: command.shell
|
|
6462
|
+
});
|
|
6463
|
+
const replacementIpcState = command.ipc ? createPmChildIpcState(replacementChild, inheritedListener) : void 0;
|
|
6464
|
+
if (replacementChild.stdout) {
|
|
6465
|
+
replacementChild.stdout.pipe(stdoutLog, { end: false });
|
|
6466
|
+
}
|
|
6467
|
+
if (replacementChild.stderr) {
|
|
6468
|
+
replacementChild.stderr.pipe(stderrLog, { end: false });
|
|
6469
|
+
}
|
|
6470
|
+
writePmLog(stdoutLog, `starting ${usesPmInheritedListener(latestRecord) ? "shared-listener" : "proxy handoff"} replacement${replacementChild.pid ? ` (pid ${replacementChild.pid})` : ""}`);
|
|
6471
|
+
const readyResult = await waitForPmChildReady(replacementMonitorRecord, replacementChild, replacementIpcState);
|
|
6472
|
+
if (!readyResult.ready) {
|
|
6473
|
+
writePmLog(stderrLog, readyResult.message ?? "replacement exited before becoming ready");
|
|
6474
|
+
replacementIpcState?.stop();
|
|
6475
|
+
await stopProxyManagedChild(replacementChild, latestRecord, stderrLog);
|
|
6476
|
+
persist((current2) => ({
|
|
6477
|
+
...current2,
|
|
6478
|
+
status: "online",
|
|
6479
|
+
proxyReadyAt: current2.proxyReadyAt,
|
|
6480
|
+
reloadRequestedAt: void 0,
|
|
6481
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6482
|
+
}));
|
|
6483
|
+
continue;
|
|
6484
|
+
}
|
|
6485
|
+
const previousChild = child;
|
|
6486
|
+
const previousControllers = controllers;
|
|
6487
|
+
const previousIpcState = childIpcState;
|
|
6488
|
+
const handoffAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6489
|
+
if (usesPmProxyController(latestRecord) && replacementTargetPort) {
|
|
6490
|
+
proxyController?.setTarget(buildPmProxyTargetUrl(latestRecord.proxy, replacementTargetPort));
|
|
6491
|
+
}
|
|
6492
|
+
persist((current2) => ({
|
|
6493
|
+
...current2,
|
|
6494
|
+
status: "online",
|
|
6495
|
+
childPid: replacementChild.pid,
|
|
6496
|
+
proxyTargetPort: replacementTargetPort,
|
|
6497
|
+
proxyReadyAt: handoffAt,
|
|
6498
|
+
startedAt: handoffAt,
|
|
6499
|
+
reloadRequestedAt: void 0,
|
|
6500
|
+
error: void 0,
|
|
6501
|
+
updatedAt: handoffAt
|
|
6502
|
+
}));
|
|
6503
|
+
if (readyResult.message) {
|
|
6504
|
+
writePmLog(stdoutLog, readyResult.message);
|
|
6505
|
+
}
|
|
6506
|
+
writePmLog(stdoutLog, `${usesPmInheritedListener(latestRecord) ? "shared listener" : "proxy handoff"} activated on ${latestRecord.proxy.host ?? "0.0.0.0"}:${latestRecord.proxy.port}`);
|
|
6507
|
+
child = replacementChild;
|
|
6508
|
+
childIpcState = replacementIpcState;
|
|
6509
|
+
childStartedAt = Date.now();
|
|
6510
|
+
activeChild = replacementChild;
|
|
6511
|
+
childWaitState = { settled: false, result: void 0 };
|
|
6512
|
+
void waitForManagedChildExit(replacementChild).then((result) => {
|
|
6513
|
+
childWaitState.result = result;
|
|
6514
|
+
childWaitState.settled = true;
|
|
6515
|
+
});
|
|
6516
|
+
controllers = await createPmChildControllers(
|
|
6517
|
+
replacementMonitorRecord,
|
|
6518
|
+
replacementChild,
|
|
6519
|
+
stdoutLog,
|
|
6520
|
+
stderrLog,
|
|
6521
|
+
requestPlannedRestart,
|
|
6522
|
+
() => {
|
|
6523
|
+
},
|
|
6524
|
+
{ ready: true, ipcController: replacementIpcState }
|
|
6525
|
+
);
|
|
6526
|
+
await delay(250);
|
|
6527
|
+
await previousControllers.stop();
|
|
6528
|
+
previousIpcState?.stop();
|
|
6529
|
+
await stopProxyManagedChild(previousChild, latestRecord, stderrLog);
|
|
6530
|
+
clearActiveChildStopTimer();
|
|
6531
|
+
if (isPmProxyOwner(latestRecord)) {
|
|
6532
|
+
syncOwnedProxyTargets(latestRecord.baseName);
|
|
6533
|
+
}
|
|
6534
|
+
continue;
|
|
6535
|
+
}
|
|
6536
|
+
await delay(25);
|
|
6537
|
+
}
|
|
6538
|
+
const exitResult = childWaitState.result;
|
|
6539
|
+
await controllers.stop();
|
|
6540
|
+
childIpcState?.stop();
|
|
4980
6541
|
clearActiveChildStopTimer();
|
|
4981
6542
|
activeChild = null;
|
|
4982
6543
|
const exitCode = waitForExit(exitResult.code, exitResult.signal);
|
|
@@ -4992,56 +6553,77 @@ error: ${text}`);
|
|
|
4992
6553
|
if (stopRequested || current.desiredState === "stopped") {
|
|
4993
6554
|
break;
|
|
4994
6555
|
}
|
|
4995
|
-
const shouldRestartForExit = plannedRestart ? true : current.restartPolicy === "always" ? true : current.restartPolicy === "on-failure" ? exitCode !== 0 || Boolean(exitResult.error) : false;
|
|
6556
|
+
const shouldRestartForExit = plannedRestart?.kind === "memory-stop" ? false : plannedRestart ? true : current.restartPolicy === "always" ? true : current.restartPolicy === "on-failure" ? exitCode !== 0 || Boolean(exitResult.error) : false;
|
|
4996
6557
|
if (!shouldRestartForExit) {
|
|
4997
6558
|
persist((latestRecord) => ({
|
|
4998
6559
|
...latestRecord,
|
|
4999
|
-
status: exitCode === 0 && !exitResult.error ? "exited" : "errored",
|
|
6560
|
+
status: plannedRestart?.kind === "memory-stop" ? "errored" : exitCode === 0 && !exitResult.error ? "exited" : "errored",
|
|
5000
6561
|
childPid: void 0,
|
|
6562
|
+
proxyTargetPort: void 0,
|
|
6563
|
+
proxyReadyAt: void 0,
|
|
5001
6564
|
runnerPid: void 0,
|
|
5002
6565
|
lastExitCode: exitCode,
|
|
5003
|
-
|
|
6566
|
+
reloadRequestedAt: void 0,
|
|
6567
|
+
error: plannedRestart?.kind === "memory-stop" ? plannedRestart.detail : exitCode === 0 && !exitResult.error ? void 0 : exitResult.error ?? `Process exited with code ${exitCode}.`,
|
|
5004
6568
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5005
6569
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5006
6570
|
}));
|
|
6571
|
+
if (isPmProxyOwner(current)) {
|
|
6572
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6573
|
+
}
|
|
5007
6574
|
return;
|
|
5008
6575
|
}
|
|
5009
6576
|
const shouldCountRestart = plannedRestart?.kind !== "watch";
|
|
5010
|
-
const baseRestartCount =
|
|
6577
|
+
const baseRestartCount = resolvePmRestartCountBase(current, wasStable, plannedRestart?.kind);
|
|
5011
6578
|
const nextRestartCount = shouldCountRestart ? baseRestartCount + 1 : current.restartCount ?? 0;
|
|
5012
6579
|
if (nextRestartCount > current.maxRestarts) {
|
|
5013
6580
|
persist((latestRecord) => ({
|
|
5014
6581
|
...latestRecord,
|
|
5015
6582
|
status: "errored",
|
|
5016
6583
|
childPid: void 0,
|
|
6584
|
+
proxyTargetPort: void 0,
|
|
6585
|
+
proxyReadyAt: void 0,
|
|
5017
6586
|
runnerPid: void 0,
|
|
5018
6587
|
restartCount: nextRestartCount,
|
|
6588
|
+
lastRestartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5019
6589
|
lastExitCode: exitCode,
|
|
6590
|
+
reloadRequestedAt: void 0,
|
|
5020
6591
|
error: plannedRestart ? `Reached max restart attempts (${current.maxRestarts}) after ${plannedRestart.kind} restart requests.` : `Reached max restart attempts (${current.maxRestarts}).`,
|
|
5021
6592
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5022
6593
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5023
6594
|
}));
|
|
5024
6595
|
writePmLog(stderrLog, `max restart attempts reached (${current.maxRestarts})`);
|
|
6596
|
+
if (isPmProxyOwner(current)) {
|
|
6597
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6598
|
+
}
|
|
5025
6599
|
return;
|
|
5026
6600
|
}
|
|
5027
6601
|
persist((latestRecord) => ({
|
|
5028
6602
|
...latestRecord,
|
|
5029
6603
|
status: "restarting",
|
|
5030
6604
|
childPid: void 0,
|
|
6605
|
+
proxyTargetPort: void 0,
|
|
6606
|
+
proxyReadyAt: void 0,
|
|
5031
6607
|
lastExitCode: exitCode,
|
|
5032
6608
|
restartCount: nextRestartCount,
|
|
6609
|
+
lastRestartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6610
|
+
reloadRequestedAt: void 0,
|
|
5033
6611
|
error: void 0,
|
|
5034
6612
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5035
6613
|
}));
|
|
6614
|
+
if (isPmProxyOwner(current)) {
|
|
6615
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6616
|
+
}
|
|
6617
|
+
const resolvedRestartDelay = resolvePmRestartDelay(current, nextRestartCount, shouldCountRestart && !wasStable && plannedRestart?.kind !== "watch");
|
|
5036
6618
|
if (plannedRestart) {
|
|
5037
6619
|
writePmLog(
|
|
5038
|
-
plannedRestart.kind === "health" ? stderrLog : stdoutLog,
|
|
5039
|
-
`restarting in ${
|
|
6620
|
+
plannedRestart.kind === "health" || plannedRestart.kind === "memory" || plannedRestart.kind === "memory-stop" || plannedRestart.kind === "startup" ? stderrLog : stdoutLog,
|
|
6621
|
+
`restarting in ${resolvedRestartDelay}ms after ${plannedRestart.kind}: ${plannedRestart.detail}`
|
|
5040
6622
|
);
|
|
5041
6623
|
} else {
|
|
5042
|
-
writePmLog(stdoutLog, `restarting in ${
|
|
6624
|
+
writePmLog(stdoutLog, `restarting in ${resolvedRestartDelay}ms`);
|
|
5043
6625
|
}
|
|
5044
|
-
await delay(
|
|
6626
|
+
await delay(resolvedRestartDelay);
|
|
5045
6627
|
}
|
|
5046
6628
|
} finally {
|
|
5047
6629
|
stopRequested = true;
|
|
@@ -5054,11 +6636,26 @@ error: ${text}`);
|
|
|
5054
6636
|
status: finalRecord.status === "errored" ? "errored" : finalRecord.status === "exited" ? "exited" : "stopped",
|
|
5055
6637
|
runnerPid: void 0,
|
|
5056
6638
|
childPid: void 0,
|
|
6639
|
+
proxyTargetPort: void 0,
|
|
6640
|
+
proxyReadyAt: void 0,
|
|
6641
|
+
reloadRequestedAt: void 0,
|
|
5057
6642
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5058
6643
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5059
6644
|
});
|
|
5060
6645
|
process.off("SIGINT", handleStopSignal);
|
|
5061
6646
|
process.off("SIGTERM", handleStopSignal);
|
|
6647
|
+
if (proxyTargetSyncTimer) {
|
|
6648
|
+
clearInterval(proxyTargetSyncTimer);
|
|
6649
|
+
proxyTargetSyncTimer = null;
|
|
6650
|
+
}
|
|
6651
|
+
if (proxyController) {
|
|
6652
|
+
await proxyController.close().catch(() => void 0);
|
|
6653
|
+
proxyController = null;
|
|
6654
|
+
}
|
|
6655
|
+
if (inheritedListener) {
|
|
6656
|
+
await new Promise((resolvePromise) => inheritedListener?.close(() => resolvePromise()));
|
|
6657
|
+
inheritedListener = null;
|
|
6658
|
+
}
|
|
5062
6659
|
await new Promise((resolvePromise) => stdoutLog.end(resolvePromise));
|
|
5063
6660
|
await new Promise((resolvePromise) => stderrLog.end(resolvePromise));
|
|
5064
6661
|
}
|
|
@@ -5112,23 +6709,26 @@ error: ${text}`);
|
|
|
5112
6709
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5113
6710
|
};
|
|
5114
6711
|
writePmRecord(match.filePath, updated);
|
|
5115
|
-
const
|
|
6712
|
+
const stopTimeout = resolvePmStopTimeout(match.record);
|
|
6713
|
+
const runnerStopped = await waitForProcessTermination(match.record.runnerPid, stopTimeout);
|
|
5116
6714
|
const childStopped = await waitForProcessTermination(
|
|
5117
6715
|
match.record.childPid,
|
|
5118
|
-
runnerStopped ? DEFAULT_PM_STOP_POLL_MS :
|
|
6716
|
+
runnerStopped ? DEFAULT_PM_STOP_POLL_MS : stopTimeout
|
|
5119
6717
|
);
|
|
5120
6718
|
if (!runnerStopped && match.record.runnerPid && isProcessAlive(match.record.runnerPid)) {
|
|
5121
|
-
terminateProcessTree(match.record.runnerPid);
|
|
6719
|
+
terminateProcessTree(match.record.runnerPid, { force: true });
|
|
5122
6720
|
await waitForProcessTermination(match.record.runnerPid, DEFAULT_PM_STOP_POLL_MS);
|
|
5123
6721
|
}
|
|
5124
6722
|
if (!childStopped && match.record.childPid && isProcessAlive(match.record.childPid)) {
|
|
5125
|
-
terminateProcessTree(match.record.childPid);
|
|
6723
|
+
terminateProcessTree(match.record.childPid, { force: true });
|
|
5126
6724
|
await waitForProcessTermination(match.record.childPid, DEFAULT_PM_STOP_POLL_MS);
|
|
5127
6725
|
}
|
|
5128
6726
|
writePmRecord(match.filePath, {
|
|
5129
6727
|
...updated,
|
|
5130
6728
|
runnerPid: void 0,
|
|
5131
6729
|
childPid: void 0,
|
|
6730
|
+
proxyTargetPort: void 0,
|
|
6731
|
+
reloadRequestedAt: void 0,
|
|
5132
6732
|
status: "stopped",
|
|
5133
6733
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5134
6734
|
});
|
|
@@ -5197,13 +6797,13 @@ error: ${text}`);
|
|
|
5197
6797
|
|
|
5198
6798
|
// src/shares/workspace-package/roots.ts
|
|
5199
6799
|
function findPackageDirectory(startDir, resolveMatch) {
|
|
5200
|
-
let currentDir =
|
|
6800
|
+
let currentDir = resolve5(startDir);
|
|
5201
6801
|
while (true) {
|
|
5202
6802
|
const match = resolveMatch(currentDir);
|
|
5203
6803
|
if (match) {
|
|
5204
6804
|
return match;
|
|
5205
6805
|
}
|
|
5206
|
-
const parentDir =
|
|
6806
|
+
const parentDir = dirname3(currentDir);
|
|
5207
6807
|
if (parentDir === currentDir) {
|
|
5208
6808
|
return void 0;
|
|
5209
6809
|
}
|
|
@@ -5231,17 +6831,17 @@ error: ${text}`);
|
|
|
5231
6831
|
function getWorkspacePackageImportCandidates(packageRoot, specifier, options = {}) {
|
|
5232
6832
|
const subpath = specifier === "elit" ? "index" : specifier.slice("elit/".length);
|
|
5233
6833
|
const builtCandidates = options.preferredBuiltFormat === "cjs" ? [
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
6834
|
+
resolve5(packageRoot, "dist", `${subpath}.cjs`),
|
|
6835
|
+
resolve5(packageRoot, "dist", `${subpath}.js`),
|
|
6836
|
+
resolve5(packageRoot, "dist", `${subpath}.mjs`)
|
|
5237
6837
|
] : [
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
6838
|
+
resolve5(packageRoot, "dist", `${subpath}.mjs`),
|
|
6839
|
+
resolve5(packageRoot, "dist", `${subpath}.js`),
|
|
6840
|
+
resolve5(packageRoot, "dist", `${subpath}.cjs`)
|
|
5241
6841
|
];
|
|
5242
6842
|
const sourceCandidates = [
|
|
5243
|
-
|
|
5244
|
-
|
|
6843
|
+
resolve5(packageRoot, "src", `${subpath}.ts`),
|
|
6844
|
+
resolve5(packageRoot, "src", `${subpath}.tsx`)
|
|
5245
6845
|
];
|
|
5246
6846
|
return options.preferBuilt ? [...builtCandidates, ...sourceCandidates] : [...sourceCandidates, ...builtCandidates];
|
|
5247
6847
|
}
|
|
@@ -5276,7 +6876,7 @@ error: ${text}`);
|
|
|
5276
6876
|
// src/shares/config/loader.ts
|
|
5277
6877
|
function resolveConfigPath(cwd = process.cwd()) {
|
|
5278
6878
|
for (const configFile of ELIT_CONFIG_FILES) {
|
|
5279
|
-
const configPath =
|
|
6879
|
+
const configPath = resolve5(cwd, configFile);
|
|
5280
6880
|
if (existsSync4(configPath)) {
|
|
5281
6881
|
return configPath;
|
|
5282
6882
|
}
|
|
@@ -5305,7 +6905,7 @@ error: ${text}`);
|
|
|
5305
6905
|
if (ext === "ts" || ext === "mts") {
|
|
5306
6906
|
try {
|
|
5307
6907
|
const { build } = await Promise.resolve().then(() => __toESM(require_main()));
|
|
5308
|
-
const configDir =
|
|
6908
|
+
const configDir = dirname3(configPath);
|
|
5309
6909
|
const tempFile = join5(configDir, `.elit-config-${Date.now()}.mjs`);
|
|
5310
6910
|
const externalAllPlugin = {
|
|
5311
6911
|
name: "external-all",
|
|
@@ -5379,6 +6979,43 @@ error: ${text}`);
|
|
|
5379
6979
|
}
|
|
5380
6980
|
|
|
5381
6981
|
// src/cli/pm/commands.ts
|
|
6982
|
+
var PM_SIGNAL_NAMES = /* @__PURE__ */ new Set([
|
|
6983
|
+
"SIGABRT",
|
|
6984
|
+
"SIGALRM",
|
|
6985
|
+
"SIGBREAK",
|
|
6986
|
+
"SIGBUS",
|
|
6987
|
+
"SIGCHLD",
|
|
6988
|
+
"SIGCONT",
|
|
6989
|
+
"SIGFPE",
|
|
6990
|
+
"SIGHUP",
|
|
6991
|
+
"SIGILL",
|
|
6992
|
+
"SIGINT",
|
|
6993
|
+
"SIGIO",
|
|
6994
|
+
"SIGIOT",
|
|
6995
|
+
"SIGKILL",
|
|
6996
|
+
"SIGPIPE",
|
|
6997
|
+
"SIGPOLL",
|
|
6998
|
+
"SIGPROF",
|
|
6999
|
+
"SIGPWR",
|
|
7000
|
+
"SIGQUIT",
|
|
7001
|
+
"SIGSEGV",
|
|
7002
|
+
"SIGSTKFLT",
|
|
7003
|
+
"SIGSTOP",
|
|
7004
|
+
"SIGSYS",
|
|
7005
|
+
"SIGTERM",
|
|
7006
|
+
"SIGTRAP",
|
|
7007
|
+
"SIGTSTP",
|
|
7008
|
+
"SIGTTIN",
|
|
7009
|
+
"SIGTTOU",
|
|
7010
|
+
"SIGUNUSED",
|
|
7011
|
+
"SIGURG",
|
|
7012
|
+
"SIGUSR1",
|
|
7013
|
+
"SIGUSR2",
|
|
7014
|
+
"SIGVTALRM",
|
|
7015
|
+
"SIGWINCH",
|
|
7016
|
+
"SIGXCPU",
|
|
7017
|
+
"SIGXFSZ"
|
|
7018
|
+
]);
|
|
5382
7019
|
async function runPmStart(args) {
|
|
5383
7020
|
const parsed = parsePmStartArgs(args);
|
|
5384
7021
|
const workspaceRoot = process.cwd();
|
|
@@ -5411,9 +7048,130 @@ error: ${text}`);
|
|
|
5411
7048
|
if (value === "all") {
|
|
5412
7049
|
return listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
5413
7050
|
}
|
|
7051
|
+
const groupMatches = findPmGroupMatches(paths, value);
|
|
7052
|
+
if (groupMatches.length > 0) {
|
|
7053
|
+
return groupMatches.map(syncPmRecordLiveness);
|
|
7054
|
+
}
|
|
5414
7055
|
const match = findPmRecordMatch(paths, value);
|
|
5415
7056
|
return match ? [syncPmRecordLiveness(match)] : [];
|
|
5416
7057
|
}
|
|
7058
|
+
function sortPmMatchesByInstance(matches) {
|
|
7059
|
+
return [...matches].sort((left, right) => left.record.instanceIndex - right.record.instanceIndex);
|
|
7060
|
+
}
|
|
7061
|
+
function resolveInspectableMatch(paths, value) {
|
|
7062
|
+
const exactMatch = findPmRecordMatch(paths, value);
|
|
7063
|
+
const groupMatches = findPmGroupMatches(paths, value).map(syncPmRecordLiveness);
|
|
7064
|
+
if (groupMatches.length > 1 && exactMatch?.record.baseName === value && exactMatch.record.name === value) {
|
|
7065
|
+
throw new Error(`Multiple managed processes found for: ${value}. Use a specific instance name such as ${groupMatches[0]?.record.name} or ${groupMatches[1]?.record.name}.`);
|
|
7066
|
+
}
|
|
7067
|
+
if (exactMatch) {
|
|
7068
|
+
return syncPmRecordLiveness(exactMatch);
|
|
7069
|
+
}
|
|
7070
|
+
return groupMatches[0];
|
|
7071
|
+
}
|
|
7072
|
+
function rebuildPmRecordDefinition(record, targetInstances = record.instances) {
|
|
7073
|
+
const definition = resolvePmAppDefinition(
|
|
7074
|
+
toPmAppConfig(record),
|
|
7075
|
+
{ name: record.baseName, env: {}, watchPaths: [], watchIgnore: [], instances: targetInstances },
|
|
7076
|
+
process.cwd(),
|
|
7077
|
+
record.source
|
|
7078
|
+
);
|
|
7079
|
+
return {
|
|
7080
|
+
...definition,
|
|
7081
|
+
name: record.name,
|
|
7082
|
+
baseName: record.baseName,
|
|
7083
|
+
instanceIndex: record.instanceIndex,
|
|
7084
|
+
instances: targetInstances
|
|
7085
|
+
};
|
|
7086
|
+
}
|
|
7087
|
+
function rebuildPmSavedDefinition(app) {
|
|
7088
|
+
const definition = resolvePmAppDefinition(
|
|
7089
|
+
toSavedPmAppConfig(app),
|
|
7090
|
+
{ name: app.baseName, env: {}, watchPaths: [], watchIgnore: [], instances: app.instances },
|
|
7091
|
+
process.cwd(),
|
|
7092
|
+
"cli"
|
|
7093
|
+
);
|
|
7094
|
+
return {
|
|
7095
|
+
...definition,
|
|
7096
|
+
name: app.name,
|
|
7097
|
+
baseName: app.baseName,
|
|
7098
|
+
instanceIndex: app.instanceIndex,
|
|
7099
|
+
instances: app.instances
|
|
7100
|
+
};
|
|
7101
|
+
}
|
|
7102
|
+
function deletePmMatches(matches) {
|
|
7103
|
+
for (const match of matches) {
|
|
7104
|
+
if ((0, import_node_fs5.existsSync)(match.record.logFiles.out)) {
|
|
7105
|
+
(0, import_node_fs5.rmSync)(match.record.logFiles.out, { force: true });
|
|
7106
|
+
}
|
|
7107
|
+
if ((0, import_node_fs5.existsSync)(match.record.logFiles.err)) {
|
|
7108
|
+
(0, import_node_fs5.rmSync)(match.record.logFiles.err, { force: true });
|
|
7109
|
+
}
|
|
7110
|
+
(0, import_node_fs5.rmSync)(match.filePath, { force: true });
|
|
7111
|
+
}
|
|
7112
|
+
}
|
|
7113
|
+
function updatePmInstanceCount(matches, instances) {
|
|
7114
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7115
|
+
for (const match of matches) {
|
|
7116
|
+
writePmRecord(match.filePath, {
|
|
7117
|
+
...match.record,
|
|
7118
|
+
instances,
|
|
7119
|
+
updatedAt: now
|
|
7120
|
+
});
|
|
7121
|
+
}
|
|
7122
|
+
}
|
|
7123
|
+
function normalizePmSignalName(value) {
|
|
7124
|
+
const trimmed = value.trim().toUpperCase();
|
|
7125
|
+
const signalName = trimmed.startsWith("SIG") ? trimmed : `SIG${trimmed}`;
|
|
7126
|
+
if (!PM_SIGNAL_NAMES.has(signalName)) {
|
|
7127
|
+
throw new Error(`Unsupported pm signal: ${value}`);
|
|
7128
|
+
}
|
|
7129
|
+
return signalName;
|
|
7130
|
+
}
|
|
7131
|
+
function resolveSignalablePid(record) {
|
|
7132
|
+
if (record.childPid && record.childPid > 0) {
|
|
7133
|
+
return record.childPid;
|
|
7134
|
+
}
|
|
7135
|
+
if (record.runnerPid && record.runnerPid > 0) {
|
|
7136
|
+
return record.runnerPid;
|
|
7137
|
+
}
|
|
7138
|
+
return void 0;
|
|
7139
|
+
}
|
|
7140
|
+
function groupPmMatchesByBaseName(matches) {
|
|
7141
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
7142
|
+
for (const match of matches) {
|
|
7143
|
+
const group = grouped.get(match.record.baseName);
|
|
7144
|
+
if (group) {
|
|
7145
|
+
group.push(match);
|
|
7146
|
+
continue;
|
|
7147
|
+
}
|
|
7148
|
+
grouped.set(match.record.baseName, [match]);
|
|
7149
|
+
}
|
|
7150
|
+
return [...grouped.entries()].sort((left, right) => left[0].localeCompare(right[0])).map(([, group]) => sortPmMatchesByInstance(group));
|
|
7151
|
+
}
|
|
7152
|
+
async function waitForPmRecordOnline(filePath, timeoutMs) {
|
|
7153
|
+
const deadline = Date.now() + timeoutMs;
|
|
7154
|
+
while (Date.now() < deadline) {
|
|
7155
|
+
if (!(0, import_node_fs5.existsSync)(filePath)) {
|
|
7156
|
+
break;
|
|
7157
|
+
}
|
|
7158
|
+
const record = readPmRecord(filePath);
|
|
7159
|
+
if (record.status === "online") {
|
|
7160
|
+
return record;
|
|
7161
|
+
}
|
|
7162
|
+
if (record.status === "errored" || record.status === "exited" || record.status === "stopped") {
|
|
7163
|
+
throw new Error(record.error ?? `Process ${record.name} failed while reloading.`);
|
|
7164
|
+
}
|
|
7165
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));
|
|
7166
|
+
}
|
|
7167
|
+
throw new Error(`Timed out waiting for the reloaded process to become online after ${timeoutMs}ms.`);
|
|
7168
|
+
}
|
|
7169
|
+
function resolvePmReloadReadyTimeout(record) {
|
|
7170
|
+
return record.waitReady || record.proxy?.strategy === "inherit" ? Math.max(record.listenTimeout + 1e3, 2e3) : Math.max(record.restartDelay + 1e3, 2e3);
|
|
7171
|
+
}
|
|
7172
|
+
function supportsPmProxyReload2(record) {
|
|
7173
|
+
return Boolean(record.proxy) && record.instances === 1 && Boolean(record.runnerPid);
|
|
7174
|
+
}
|
|
5417
7175
|
function padCell(value, width) {
|
|
5418
7176
|
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
5419
7177
|
}
|
|
@@ -5424,8 +7182,255 @@ error: ${text}`);
|
|
|
5424
7182
|
const lines = (0, import_node_fs5.readFileSync)(filePath, "utf8").split(/\r?\n/).filter((line) => line.length > 0);
|
|
5425
7183
|
return lines.slice(-lineCount).join(import_node_os2.EOL);
|
|
5426
7184
|
}
|
|
5427
|
-
function
|
|
5428
|
-
|
|
7185
|
+
function listPmMatches(paths) {
|
|
7186
|
+
return listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
7187
|
+
}
|
|
7188
|
+
function isPmRecordActive(record) {
|
|
7189
|
+
return record.desiredState === "running" && (record.status === "starting" || record.status === "online" || record.status === "restarting");
|
|
7190
|
+
}
|
|
7191
|
+
function resolvePmUptimeMs(record) {
|
|
7192
|
+
if (!isPmRecordActive(record) || !record.startedAt) {
|
|
7193
|
+
return void 0;
|
|
7194
|
+
}
|
|
7195
|
+
const startedTime = Date.parse(record.startedAt);
|
|
7196
|
+
if (Number.isNaN(startedTime)) {
|
|
7197
|
+
return void 0;
|
|
7198
|
+
}
|
|
7199
|
+
return Math.max(0, Date.now() - startedTime);
|
|
7200
|
+
}
|
|
7201
|
+
function resolvePmLiveMetrics(record) {
|
|
7202
|
+
const uptimeMs = resolvePmUptimeMs(record);
|
|
7203
|
+
if (!isPmRecordActive(record) || !record.childPid) {
|
|
7204
|
+
return { uptimeMs };
|
|
7205
|
+
}
|
|
7206
|
+
const sampledMetrics = samplePmProcessMetrics(record.childPid);
|
|
7207
|
+
return {
|
|
7208
|
+
...sampledMetrics,
|
|
7209
|
+
uptimeMs,
|
|
7210
|
+
updatedAt: sampledMetrics.cpuPercent !== void 0 || sampledMetrics.memoryRssBytes !== void 0 ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
7211
|
+
};
|
|
7212
|
+
}
|
|
7213
|
+
function toPmDisplayRecord(record) {
|
|
7214
|
+
return {
|
|
7215
|
+
record,
|
|
7216
|
+
liveMetrics: resolvePmLiveMetrics(record)
|
|
7217
|
+
};
|
|
7218
|
+
}
|
|
7219
|
+
function serializePmRecord(record) {
|
|
7220
|
+
return {
|
|
7221
|
+
...record,
|
|
7222
|
+
liveMetrics: resolvePmLiveMetrics(record)
|
|
7223
|
+
};
|
|
7224
|
+
}
|
|
7225
|
+
function parsePmFormatOption(args, index, option) {
|
|
7226
|
+
let value;
|
|
7227
|
+
if (option.startsWith("--format=")) {
|
|
7228
|
+
value = option.slice("--format=".length);
|
|
7229
|
+
} else {
|
|
7230
|
+
value = readRequiredValue(args, index + 1, "--format");
|
|
7231
|
+
index += 1;
|
|
7232
|
+
}
|
|
7233
|
+
if (value !== "table" && value !== "json") {
|
|
7234
|
+
throw new Error(`Unsupported pm output format: ${value}`);
|
|
7235
|
+
}
|
|
7236
|
+
return {
|
|
7237
|
+
format: value,
|
|
7238
|
+
nextIndex: index
|
|
7239
|
+
};
|
|
7240
|
+
}
|
|
7241
|
+
function parsePmListArgs(args) {
|
|
7242
|
+
let format = "table";
|
|
7243
|
+
for (let index = 0; index < args.length; index++) {
|
|
7244
|
+
const arg = args[index];
|
|
7245
|
+
switch (arg) {
|
|
7246
|
+
case "--json":
|
|
7247
|
+
format = "json";
|
|
7248
|
+
break;
|
|
7249
|
+
case "--format":
|
|
7250
|
+
default:
|
|
7251
|
+
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
7252
|
+
const parsed = parsePmFormatOption(args, index, arg);
|
|
7253
|
+
format = parsed.format;
|
|
7254
|
+
index = parsed.nextIndex;
|
|
7255
|
+
break;
|
|
7256
|
+
}
|
|
7257
|
+
throw new Error(`Unknown pm list option: ${arg}`);
|
|
7258
|
+
}
|
|
7259
|
+
}
|
|
7260
|
+
return { format };
|
|
7261
|
+
}
|
|
7262
|
+
function parsePmShowArgs(args) {
|
|
7263
|
+
let format = "text";
|
|
7264
|
+
let name;
|
|
7265
|
+
for (let index = 0; index < args.length; index++) {
|
|
7266
|
+
const arg = args[index];
|
|
7267
|
+
switch (arg) {
|
|
7268
|
+
case "--json":
|
|
7269
|
+
format = "json";
|
|
7270
|
+
break;
|
|
7271
|
+
case "--format":
|
|
7272
|
+
default:
|
|
7273
|
+
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
7274
|
+
const parsed = parsePmFormatOption(args, index, arg);
|
|
7275
|
+
format = parsed.format === "json" ? "json" : "text";
|
|
7276
|
+
index = parsed.nextIndex;
|
|
7277
|
+
break;
|
|
7278
|
+
}
|
|
7279
|
+
if (arg.startsWith("-")) {
|
|
7280
|
+
throw new Error(`Unknown pm show option: ${arg}`);
|
|
7281
|
+
}
|
|
7282
|
+
if (name) {
|
|
7283
|
+
throw new Error("pm show accepts exactly one process name.");
|
|
7284
|
+
}
|
|
7285
|
+
name = arg;
|
|
7286
|
+
break;
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7289
|
+
if (!name) {
|
|
7290
|
+
throw new Error("Usage: elit pm show <name> [--json]");
|
|
7291
|
+
}
|
|
7292
|
+
return { name, format };
|
|
7293
|
+
}
|
|
7294
|
+
function formatPmDuration(durationMs) {
|
|
7295
|
+
if (durationMs < 1e3) {
|
|
7296
|
+
return `${durationMs}ms`;
|
|
7297
|
+
}
|
|
7298
|
+
const totalSeconds = Math.floor(durationMs / 1e3);
|
|
7299
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
7300
|
+
const hours = Math.floor(totalSeconds % 86400 / 3600);
|
|
7301
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
7302
|
+
const seconds = totalSeconds % 60;
|
|
7303
|
+
const parts = [];
|
|
7304
|
+
if (days > 0) {
|
|
7305
|
+
parts.push(`${days}d`);
|
|
7306
|
+
}
|
|
7307
|
+
if (hours > 0) {
|
|
7308
|
+
parts.push(`${hours}h`);
|
|
7309
|
+
}
|
|
7310
|
+
if (minutes > 0) {
|
|
7311
|
+
parts.push(`${minutes}m`);
|
|
7312
|
+
}
|
|
7313
|
+
if (seconds > 0 || parts.length === 0) {
|
|
7314
|
+
parts.push(`${seconds}s`);
|
|
7315
|
+
}
|
|
7316
|
+
return parts.slice(0, 2).join(" ");
|
|
7317
|
+
}
|
|
7318
|
+
function formatPmCpuPercent(cpuPercent) {
|
|
7319
|
+
if (cpuPercent === void 0 || !Number.isFinite(cpuPercent)) {
|
|
7320
|
+
return "-";
|
|
7321
|
+
}
|
|
7322
|
+
return `${cpuPercent >= 100 ? cpuPercent.toFixed(0) : cpuPercent.toFixed(1)}%`;
|
|
7323
|
+
}
|
|
7324
|
+
function formatPmMemory(memoryRssBytes) {
|
|
7325
|
+
if (memoryRssBytes === void 0 || !Number.isFinite(memoryRssBytes) || memoryRssBytes < 0) {
|
|
7326
|
+
return "-";
|
|
7327
|
+
}
|
|
7328
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
7329
|
+
let value = memoryRssBytes;
|
|
7330
|
+
let unitIndex = 0;
|
|
7331
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
7332
|
+
value /= 1024;
|
|
7333
|
+
unitIndex += 1;
|
|
7334
|
+
}
|
|
7335
|
+
const formatted = value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1);
|
|
7336
|
+
return `${formatted}${units[unitIndex]}`;
|
|
7337
|
+
}
|
|
7338
|
+
function formatPmUptime(uptimeMs) {
|
|
7339
|
+
if (uptimeMs === void 0 || !Number.isFinite(uptimeMs)) {
|
|
7340
|
+
return "-";
|
|
7341
|
+
}
|
|
7342
|
+
return formatPmDuration(Math.max(0, uptimeMs));
|
|
7343
|
+
}
|
|
7344
|
+
function formatPmTarget(record) {
|
|
7345
|
+
if (record.script) {
|
|
7346
|
+
return record.script;
|
|
7347
|
+
}
|
|
7348
|
+
if (record.file) {
|
|
7349
|
+
return record.file;
|
|
7350
|
+
}
|
|
7351
|
+
if (record.wapk) {
|
|
7352
|
+
return record.wapk;
|
|
7353
|
+
}
|
|
7354
|
+
return "-";
|
|
7355
|
+
}
|
|
7356
|
+
function pushPmDetail(lines, label, value) {
|
|
7357
|
+
lines.push(`${padCell(`${label}:`, 18)} ${value}`);
|
|
7358
|
+
}
|
|
7359
|
+
function pushPmDetailList(lines, label, values) {
|
|
7360
|
+
if (values.length === 0) {
|
|
7361
|
+
pushPmDetail(lines, label, "-");
|
|
7362
|
+
return;
|
|
7363
|
+
}
|
|
7364
|
+
pushPmDetail(lines, label, values[0] ?? "-");
|
|
7365
|
+
for (const value of values.slice(1)) {
|
|
7366
|
+
lines.push(`${" ".repeat(20)} ${value}`);
|
|
7367
|
+
}
|
|
7368
|
+
}
|
|
7369
|
+
function formatPmRecordDetails(record, liveMetrics) {
|
|
7370
|
+
const lines = [`Process: ${record.name}`];
|
|
7371
|
+
pushPmDetail(lines, "id", record.id);
|
|
7372
|
+
pushPmDetail(lines, "status", record.status);
|
|
7373
|
+
pushPmDetail(lines, "desired state", record.desiredState);
|
|
7374
|
+
pushPmDetail(lines, "instance", `${record.instanceIndex}/${record.instances}`);
|
|
7375
|
+
pushPmDetail(lines, "cpu", formatPmCpuPercent(liveMetrics.cpuPercent));
|
|
7376
|
+
pushPmDetail(lines, "memory", formatPmMemory(liveMetrics.memoryRssBytes));
|
|
7377
|
+
pushPmDetail(lines, "uptime", formatPmUptime(liveMetrics.uptimeMs));
|
|
7378
|
+
pushPmDetail(lines, "type", record.type);
|
|
7379
|
+
pushPmDetail(lines, "source", record.source);
|
|
7380
|
+
pushPmDetail(lines, "runtime", record.runtime ?? "-");
|
|
7381
|
+
pushPmDetail(lines, "cwd", record.cwd);
|
|
7382
|
+
pushPmDetail(lines, "target", formatPmTarget(record));
|
|
7383
|
+
pushPmDetail(lines, "command", record.commandPreview || "-");
|
|
7384
|
+
pushPmDetail(lines, "runner pid", record.runnerPid ? String(record.runnerPid) : "-");
|
|
7385
|
+
pushPmDetail(lines, "child pid", record.childPid ? String(record.childPid) : "-");
|
|
7386
|
+
pushPmDetail(lines, "restart count", `${record.restartCount}/${record.maxRestarts}`);
|
|
7387
|
+
pushPmDetail(lines, "restart policy", record.restartPolicy);
|
|
7388
|
+
pushPmDetail(lines, "proxy", record.proxy ? `http://${record.proxy.host ?? "0.0.0.0"}:${record.proxy.port}` : "-");
|
|
7389
|
+
pushPmDetail(lines, "proxy strategy", record.proxy?.strategy ?? "-");
|
|
7390
|
+
pushPmDetail(lines, "proxy target", record.proxy && record.proxyTargetPort ? `${record.proxy.targetHost ?? "127.0.0.1"}:${record.proxyTargetPort}` : "-");
|
|
7391
|
+
pushPmDetail(lines, "max memory", record.maxMemoryBytes ? formatPmMemory(record.maxMemoryBytes) : "-");
|
|
7392
|
+
pushPmDetail(lines, "memory action", record.memoryAction ?? "-");
|
|
7393
|
+
pushPmDetail(lines, "cron restart", record.cronRestart ?? "-");
|
|
7394
|
+
pushPmDetail(lines, "exp backoff", record.expBackoffRestartDelay ? formatPmDuration(record.expBackoffRestartDelay) : "-");
|
|
7395
|
+
pushPmDetail(lines, "exp backoff max", record.expBackoffRestartMaxDelay ? formatPmDuration(record.expBackoffRestartMaxDelay) : "-");
|
|
7396
|
+
pushPmDetail(lines, "restart window", record.restartWindow ? formatPmDuration(record.restartWindow) : "-");
|
|
7397
|
+
pushPmDetail(lines, "wait ready", record.waitReady ? "enabled" : "disabled");
|
|
7398
|
+
pushPmDetail(lines, "listen timeout", record.waitReady ? formatPmDuration(record.listenTimeout) : "-");
|
|
7399
|
+
pushPmDetail(lines, "restart delay", formatPmDuration(record.restartDelay));
|
|
7400
|
+
pushPmDetail(lines, "kill timeout", formatPmDuration(record.killTimeout));
|
|
7401
|
+
pushPmDetail(lines, "min uptime", formatPmDuration(record.minUptime));
|
|
7402
|
+
pushPmDetail(lines, "autorestart", record.autorestart ? "enabled" : "disabled");
|
|
7403
|
+
pushPmDetail(lines, "watch", record.watch ? "enabled" : "disabled");
|
|
7404
|
+
pushPmDetail(lines, "watch debounce", record.watch ? formatPmDuration(record.watchDebounce) : "-");
|
|
7405
|
+
pushPmDetailList(lines, "watch paths", record.watchPaths);
|
|
7406
|
+
pushPmDetailList(lines, "watch ignore", record.watchIgnore);
|
|
7407
|
+
if (record.healthCheck) {
|
|
7408
|
+
pushPmDetail(lines, "health check", record.healthCheck.url);
|
|
7409
|
+
pushPmDetail(lines, "health grace", formatPmDuration(record.healthCheck.gracePeriod));
|
|
7410
|
+
pushPmDetail(lines, "health interval", formatPmDuration(record.healthCheck.interval));
|
|
7411
|
+
pushPmDetail(lines, "health timeout", formatPmDuration(record.healthCheck.timeout));
|
|
7412
|
+
pushPmDetail(lines, "health failures", String(record.healthCheck.maxFailures));
|
|
7413
|
+
} else {
|
|
7414
|
+
pushPmDetail(lines, "health check", "-");
|
|
7415
|
+
}
|
|
7416
|
+
pushPmDetailList(lines, "env", Object.entries(record.env).map(([key, value]) => `${key}=${value}`));
|
|
7417
|
+
pushPmDetail(lines, "stdout log", record.logFiles.out);
|
|
7418
|
+
pushPmDetail(lines, "stderr log", record.logFiles.err);
|
|
7419
|
+
pushPmDetail(lines, "created at", record.createdAt);
|
|
7420
|
+
pushPmDetail(lines, "updated at", record.updatedAt);
|
|
7421
|
+
pushPmDetail(lines, "metrics at", liveMetrics.updatedAt ?? "-");
|
|
7422
|
+
pushPmDetail(lines, "started at", record.startedAt ?? "-");
|
|
7423
|
+
pushPmDetail(lines, "stopped at", record.stoppedAt ?? "-");
|
|
7424
|
+
pushPmDetail(lines, "last exit", record.lastExitCode === void 0 ? "-" : String(record.lastExitCode));
|
|
7425
|
+
pushPmDetail(lines, "error", record.error ?? "-");
|
|
7426
|
+
return lines.join(import_node_os2.EOL);
|
|
7427
|
+
}
|
|
7428
|
+
function printPmList(paths, format = "table") {
|
|
7429
|
+
const matches = listPmMatches(paths).map((match) => toPmDisplayRecord(match.record));
|
|
7430
|
+
if (format === "json") {
|
|
7431
|
+
console.log(JSON.stringify(matches.map((match) => ({ ...match.record, liveMetrics: match.liveMetrics })), null, 2));
|
|
7432
|
+
return;
|
|
7433
|
+
}
|
|
5429
7434
|
if (matches.length === 0) {
|
|
5430
7435
|
console.log("No managed processes found.");
|
|
5431
7436
|
return;
|
|
@@ -5434,22 +7439,47 @@ error: ${text}`);
|
|
|
5434
7439
|
padCell("name", 20),
|
|
5435
7440
|
padCell("status", 12),
|
|
5436
7441
|
padCell("pid", 8),
|
|
7442
|
+
padCell("cpu", 8),
|
|
7443
|
+
padCell("memory", 10),
|
|
7444
|
+
padCell("uptime", 10),
|
|
5437
7445
|
padCell("restarts", 10),
|
|
5438
7446
|
padCell("type", 8),
|
|
5439
7447
|
"runtime"
|
|
5440
7448
|
];
|
|
5441
7449
|
console.log(headers.join(" "));
|
|
5442
|
-
for (const { record } of matches) {
|
|
7450
|
+
for (const { record, liveMetrics } of matches) {
|
|
5443
7451
|
console.log([
|
|
5444
7452
|
padCell(record.name, 20),
|
|
5445
7453
|
padCell(record.status, 12),
|
|
5446
7454
|
padCell(record.childPid ? String(record.childPid) : "-", 8),
|
|
7455
|
+
padCell(formatPmCpuPercent(liveMetrics.cpuPercent), 8),
|
|
7456
|
+
padCell(formatPmMemory(liveMetrics.memoryRssBytes), 10),
|
|
7457
|
+
padCell(formatPmUptime(liveMetrics.uptimeMs), 10),
|
|
5447
7458
|
padCell(String(record.restartCount ?? 0), 10),
|
|
5448
7459
|
padCell(record.type, 8),
|
|
5449
7460
|
record.runtime ?? "-"
|
|
5450
7461
|
].join(" "));
|
|
5451
7462
|
}
|
|
5452
7463
|
}
|
|
7464
|
+
async function runPmList(args) {
|
|
7465
|
+
const options = parsePmListArgs(args);
|
|
7466
|
+
const { paths } = await loadPmContext();
|
|
7467
|
+
printPmList(paths, options.format);
|
|
7468
|
+
}
|
|
7469
|
+
async function runPmShow(args) {
|
|
7470
|
+
const options = parsePmShowArgs(args);
|
|
7471
|
+
const { paths } = await loadPmContext();
|
|
7472
|
+
const match = resolveInspectableMatch(paths, options.name);
|
|
7473
|
+
if (!match) {
|
|
7474
|
+
throw new Error(`No managed process found for: ${options.name}`);
|
|
7475
|
+
}
|
|
7476
|
+
const synced = syncPmRecordLiveness(match);
|
|
7477
|
+
if (options.format === "json") {
|
|
7478
|
+
console.log(JSON.stringify(serializePmRecord(synced.record), null, 2));
|
|
7479
|
+
return;
|
|
7480
|
+
}
|
|
7481
|
+
console.log(formatPmRecordDetails(synced.record, resolvePmLiveMetrics(synced.record)));
|
|
7482
|
+
}
|
|
5453
7483
|
async function runPmStop(args) {
|
|
5454
7484
|
const target = args[0];
|
|
5455
7485
|
if (!target) {
|
|
@@ -5476,17 +7506,62 @@ error: ${text}`);
|
|
|
5476
7506
|
await stopPmMatches(matches);
|
|
5477
7507
|
const restarted = [];
|
|
5478
7508
|
for (const match of matches) {
|
|
5479
|
-
const definition =
|
|
5480
|
-
toPmAppConfig(match.record),
|
|
5481
|
-
{ name: match.record.name, env: {}, watchPaths: [], watchIgnore: [] },
|
|
5482
|
-
process.cwd(),
|
|
5483
|
-
match.record.source
|
|
5484
|
-
);
|
|
7509
|
+
const definition = rebuildPmRecordDefinition(match.record);
|
|
5485
7510
|
await startManagedProcess(definition, paths);
|
|
5486
7511
|
restarted.push(match.record.name);
|
|
5487
7512
|
}
|
|
5488
7513
|
console.log(`[pm] restarted ${restarted.join(", ")}`);
|
|
5489
7514
|
}
|
|
7515
|
+
async function runPmReload(args) {
|
|
7516
|
+
const target = args[0];
|
|
7517
|
+
if (!target) {
|
|
7518
|
+
throw new Error("Usage: elit pm reload <name|all>");
|
|
7519
|
+
}
|
|
7520
|
+
const { paths } = await loadPmContext();
|
|
7521
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7522
|
+
if (matches.length === 0) {
|
|
7523
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7524
|
+
}
|
|
7525
|
+
const reloaded = [];
|
|
7526
|
+
const errors = [];
|
|
7527
|
+
for (const group of groupPmMatchesByBaseName(matches)) {
|
|
7528
|
+
for (const match of group) {
|
|
7529
|
+
try {
|
|
7530
|
+
if (supportsPmProxyReload2(match.record)) {
|
|
7531
|
+
const reloadRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7532
|
+
writePmRecord(match.filePath, {
|
|
7533
|
+
...match.record,
|
|
7534
|
+
status: "restarting",
|
|
7535
|
+
reloadRequestedAt,
|
|
7536
|
+
updatedAt: reloadRequestedAt,
|
|
7537
|
+
error: void 0
|
|
7538
|
+
});
|
|
7539
|
+
await waitForPmRecordOnline(
|
|
7540
|
+
getPmRecordPath(paths, match.record.id),
|
|
7541
|
+
resolvePmReloadReadyTimeout(match.record)
|
|
7542
|
+
);
|
|
7543
|
+
reloaded.push(match.record.name);
|
|
7544
|
+
continue;
|
|
7545
|
+
}
|
|
7546
|
+
await stopPmMatches([match]);
|
|
7547
|
+
const definition = rebuildPmRecordDefinition(match.record);
|
|
7548
|
+
const startedRecord = await startManagedProcess(definition, paths);
|
|
7549
|
+
await waitForPmRecordOnline(
|
|
7550
|
+
getPmRecordPath(paths, startedRecord.id),
|
|
7551
|
+
resolvePmReloadReadyTimeout(startedRecord)
|
|
7552
|
+
);
|
|
7553
|
+
reloaded.push(match.record.name);
|
|
7554
|
+
} catch (error) {
|
|
7555
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7556
|
+
errors.push(`[pm] ${match.record.name}: ${message}`);
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
if (errors.length > 0) {
|
|
7561
|
+
throw new Error([`[pm] reloaded ${reloaded.length} process${reloaded.length === 1 ? "" : "es"}`, ...errors].join(import_node_os2.EOL));
|
|
7562
|
+
}
|
|
7563
|
+
console.log(`[pm] reloaded ${reloaded.join(", ")}`);
|
|
7564
|
+
}
|
|
5490
7565
|
async function runPmSave() {
|
|
5491
7566
|
const { paths } = await loadPmContext();
|
|
5492
7567
|
ensurePmDirectories(paths);
|
|
@@ -5508,12 +7583,7 @@ error: ${text}`);
|
|
|
5508
7583
|
let restored = 0;
|
|
5509
7584
|
for (const app of dump.apps) {
|
|
5510
7585
|
try {
|
|
5511
|
-
const definition =
|
|
5512
|
-
toSavedPmAppConfig(app),
|
|
5513
|
-
{ name: app.name, env: {}, watchPaths: [], watchIgnore: [] },
|
|
5514
|
-
process.cwd(),
|
|
5515
|
-
"cli"
|
|
5516
|
-
);
|
|
7586
|
+
const definition = rebuildPmSavedDefinition(app);
|
|
5517
7587
|
await startManagedProcess(definition, paths);
|
|
5518
7588
|
restored += 1;
|
|
5519
7589
|
} catch (error) {
|
|
@@ -5537,16 +7607,127 @@ error: ${text}`);
|
|
|
5537
7607
|
throw new Error(`No managed process found for: ${target}`);
|
|
5538
7608
|
}
|
|
5539
7609
|
await stopPmMatches(matches);
|
|
7610
|
+
deletePmMatches(matches);
|
|
7611
|
+
console.log(`[pm] deleted ${matches.length} process${matches.length === 1 ? "" : "es"}`);
|
|
7612
|
+
}
|
|
7613
|
+
async function runPmScale(args) {
|
|
7614
|
+
const target = args[0];
|
|
7615
|
+
const countArg = args[1];
|
|
7616
|
+
if (!target || countArg === void 0 || args.length > 2) {
|
|
7617
|
+
throw new Error("Usage: elit pm scale <name> <count>");
|
|
7618
|
+
}
|
|
7619
|
+
const desiredCount = normalizeIntegerOption(countArg, "pm scale <count>", 0);
|
|
7620
|
+
const { config, paths } = await loadPmContext();
|
|
7621
|
+
const exactMatch = findPmRecordMatch(paths, target);
|
|
7622
|
+
const currentMatches = sortPmMatchesByInstance(resolveNamedMatches(paths, exactMatch?.record.baseName ?? target));
|
|
7623
|
+
if (currentMatches.length === 0) {
|
|
7624
|
+
if (desiredCount === 0) {
|
|
7625
|
+
console.log(`[pm] ${target} already scaled to 0 instances`);
|
|
7626
|
+
return;
|
|
7627
|
+
}
|
|
7628
|
+
const definitions = resolvePmStartDefinitions(
|
|
7629
|
+
{ name: target, env: {}, watchPaths: [], watchIgnore: [], instances: desiredCount },
|
|
7630
|
+
config,
|
|
7631
|
+
process.cwd()
|
|
7632
|
+
);
|
|
7633
|
+
for (const definition of definitions) {
|
|
7634
|
+
await startManagedProcess(definition, paths);
|
|
7635
|
+
}
|
|
7636
|
+
console.log(`[pm] scaled ${target} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7637
|
+
return;
|
|
7638
|
+
}
|
|
7639
|
+
const baseName = currentMatches[0]?.record.baseName ?? target;
|
|
7640
|
+
if (desiredCount === currentMatches.length) {
|
|
7641
|
+
console.log(`[pm] ${baseName} already scaled to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7642
|
+
return;
|
|
7643
|
+
}
|
|
7644
|
+
if (desiredCount === 0) {
|
|
7645
|
+
await stopPmMatches(currentMatches);
|
|
7646
|
+
deletePmMatches(currentMatches);
|
|
7647
|
+
console.log(`[pm] scaled ${baseName} to 0 instances`);
|
|
7648
|
+
return;
|
|
7649
|
+
}
|
|
7650
|
+
if (desiredCount < currentMatches.length) {
|
|
7651
|
+
const toRemove = [...currentMatches].sort((left, right) => right.record.instanceIndex - left.record.instanceIndex).slice(0, currentMatches.length - desiredCount);
|
|
7652
|
+
const remaining = currentMatches.filter((match) => !toRemove.some((removal) => removal.record.id === match.record.id));
|
|
7653
|
+
await stopPmMatches(toRemove);
|
|
7654
|
+
deletePmMatches(toRemove);
|
|
7655
|
+
updatePmInstanceCount(remaining, desiredCount);
|
|
7656
|
+
console.log(`[pm] scaled ${baseName} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7657
|
+
return;
|
|
7658
|
+
}
|
|
7659
|
+
updatePmInstanceCount(currentMatches, desiredCount);
|
|
7660
|
+
const baseRecord = currentMatches[0]?.record;
|
|
7661
|
+
if (!baseRecord) {
|
|
7662
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7663
|
+
}
|
|
7664
|
+
const baseDefinition = rebuildPmRecordDefinition(baseRecord, desiredCount);
|
|
7665
|
+
const expandedDefinitions = expandPmInstanceDefinitions(baseDefinition, desiredCount);
|
|
7666
|
+
const existingNames = new Set(currentMatches.map((match) => match.record.name));
|
|
7667
|
+
for (const definition of expandedDefinitions) {
|
|
7668
|
+
if (existingNames.has(definition.name)) {
|
|
7669
|
+
continue;
|
|
7670
|
+
}
|
|
7671
|
+
await startManagedProcess(definition, paths);
|
|
7672
|
+
}
|
|
7673
|
+
console.log(`[pm] scaled ${baseName} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7674
|
+
}
|
|
7675
|
+
async function runPmSendSignal(args) {
|
|
7676
|
+
const signalArg = args[0];
|
|
7677
|
+
const target = args[1];
|
|
7678
|
+
if (!signalArg || !target || args.length > 2) {
|
|
7679
|
+
throw new Error("Usage: elit pm send-signal <signal> <name|all>");
|
|
7680
|
+
}
|
|
7681
|
+
const signalName = normalizePmSignalName(signalArg);
|
|
7682
|
+
const { paths } = await loadPmContext();
|
|
7683
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7684
|
+
if (matches.length === 0) {
|
|
7685
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7686
|
+
}
|
|
7687
|
+
let signaled = 0;
|
|
7688
|
+
const errors = [];
|
|
5540
7689
|
for (const match of matches) {
|
|
5541
|
-
|
|
5542
|
-
|
|
7690
|
+
const pid = resolveSignalablePid(match.record);
|
|
7691
|
+
if (!pid) {
|
|
7692
|
+
continue;
|
|
5543
7693
|
}
|
|
5544
|
-
|
|
5545
|
-
(
|
|
7694
|
+
try {
|
|
7695
|
+
sendPmSignal(pid, signalName);
|
|
7696
|
+
signaled += 1;
|
|
7697
|
+
} catch (error) {
|
|
7698
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7699
|
+
errors.push(`[pm] ${match.record.name}: ${message}`);
|
|
5546
7700
|
}
|
|
5547
|
-
(0, import_node_fs5.rmSync)(match.filePath, { force: true });
|
|
5548
7701
|
}
|
|
5549
|
-
|
|
7702
|
+
if (errors.length > 0) {
|
|
7703
|
+
throw new Error([`[pm] sent ${signalName} to ${signaled} process${signaled === 1 ? "" : "es"}`, ...errors].join(import_node_os2.EOL));
|
|
7704
|
+
}
|
|
7705
|
+
if (signaled === 0) {
|
|
7706
|
+
throw new Error(`No running managed process found for: ${target}`);
|
|
7707
|
+
}
|
|
7708
|
+
console.log(`[pm] sent ${signalName} to ${signaled} process${signaled === 1 ? "" : "es"}`);
|
|
7709
|
+
}
|
|
7710
|
+
async function runPmReset(args) {
|
|
7711
|
+
const target = args[0];
|
|
7712
|
+
if (!target) {
|
|
7713
|
+
throw new Error("Usage: elit pm reset <name|all>");
|
|
7714
|
+
}
|
|
7715
|
+
const { paths } = await loadPmContext();
|
|
7716
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7717
|
+
if (matches.length === 0) {
|
|
7718
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7719
|
+
}
|
|
7720
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7721
|
+
for (const match of matches) {
|
|
7722
|
+
writePmRecord(match.filePath, {
|
|
7723
|
+
...match.record,
|
|
7724
|
+
restartCount: 0,
|
|
7725
|
+
lastExitCode: void 0,
|
|
7726
|
+
error: void 0,
|
|
7727
|
+
updatedAt: now
|
|
7728
|
+
});
|
|
7729
|
+
}
|
|
7730
|
+
console.log(`[pm] reset ${matches.length} process${matches.length === 1 ? "" : "es"}`);
|
|
5550
7731
|
}
|
|
5551
7732
|
async function runPmLogs(args) {
|
|
5552
7733
|
if (args.length === 0) {
|
|
@@ -5579,7 +7760,7 @@ error: ${text}`);
|
|
|
5579
7760
|
throw new Error("Usage: elit pm logs <name> [--lines <n>] [--stderr]");
|
|
5580
7761
|
}
|
|
5581
7762
|
const { paths } = await loadPmContext();
|
|
5582
|
-
const match =
|
|
7763
|
+
const match = resolveInspectableMatch(paths, name);
|
|
5583
7764
|
if (!match) {
|
|
5584
7765
|
throw new Error(`No managed process found for: ${name}`);
|
|
5585
7766
|
}
|
|
@@ -5603,17 +7784,36 @@ error: ${text}`);
|
|
|
5603
7784
|
await runPmStart(args.slice(1));
|
|
5604
7785
|
return;
|
|
5605
7786
|
case "list":
|
|
5606
|
-
case "ls":
|
|
5607
|
-
|
|
5608
|
-
|
|
7787
|
+
case "ls":
|
|
7788
|
+
await runPmList(args.slice(1));
|
|
7789
|
+
return;
|
|
7790
|
+
case "jlist":
|
|
7791
|
+
await runPmList(["--json", ...args.slice(1)]);
|
|
7792
|
+
return;
|
|
7793
|
+
case "show":
|
|
7794
|
+
case "describe":
|
|
7795
|
+
await runPmShow(args.slice(1));
|
|
5609
7796
|
return;
|
|
5610
|
-
}
|
|
5611
7797
|
case "stop":
|
|
5612
7798
|
await runPmStop(args.slice(1));
|
|
5613
7799
|
return;
|
|
5614
7800
|
case "restart":
|
|
5615
7801
|
await runPmRestart(args.slice(1));
|
|
5616
7802
|
return;
|
|
7803
|
+
case "reload":
|
|
7804
|
+
await runPmReload(args.slice(1));
|
|
7805
|
+
return;
|
|
7806
|
+
case "scale":
|
|
7807
|
+
await runPmScale(args.slice(1));
|
|
7808
|
+
return;
|
|
7809
|
+
case "send-signal":
|
|
7810
|
+
case "signal":
|
|
7811
|
+
case "sendSignal":
|
|
7812
|
+
await runPmSendSignal(args.slice(1));
|
|
7813
|
+
return;
|
|
7814
|
+
case "reset":
|
|
7815
|
+
await runPmReset(args.slice(1));
|
|
7816
|
+
return;
|
|
5617
7817
|
case "delete":
|
|
5618
7818
|
case "remove":
|
|
5619
7819
|
case "rm":
|