elit 3.6.7 → 3.6.8
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 +2496 -303
- package/dist/cli.mjs +2501 -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 +2101 -134
- package/dist/pm.d.ts +83 -8
- package/dist/pm.js +2095 -158
- package/dist/pm.mjs +2091 -139
- 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,9 +5199,210 @@ error: ${text}`);
|
|
|
4655
5199
|
return watcher;
|
|
4656
5200
|
}
|
|
4657
5201
|
|
|
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
|
+
function resolvePmProxyHost(proxy) {
|
|
5207
|
+
return proxy.host?.trim() || "0.0.0.0";
|
|
5208
|
+
}
|
|
5209
|
+
function resolvePmProxyTargetHost(proxy) {
|
|
5210
|
+
return proxy.targetHost?.trim() || "127.0.0.1";
|
|
5211
|
+
}
|
|
5212
|
+
function resolvePmProxyEnvVar(proxy) {
|
|
5213
|
+
return proxy.envVar?.trim() || "PORT";
|
|
5214
|
+
}
|
|
5215
|
+
function buildPmProxyTargetUrl(proxy, targetPort) {
|
|
5216
|
+
return `http://${resolvePmProxyTargetHost(proxy)}:${targetPort}`;
|
|
5217
|
+
}
|
|
5218
|
+
function rewritePmProxyHealthCheckUrl(url, targetHost, targetPort) {
|
|
5219
|
+
const targetUrl = new URL(url);
|
|
5220
|
+
targetUrl.hostname = targetHost;
|
|
5221
|
+
targetUrl.port = String(targetPort);
|
|
5222
|
+
return targetUrl.toString();
|
|
5223
|
+
}
|
|
5224
|
+
async function allocatePmProxyTargetPort(host = "127.0.0.1") {
|
|
5225
|
+
const server = (0, import_node_net.createServer)();
|
|
5226
|
+
return await new Promise((resolve7, reject) => {
|
|
5227
|
+
server.once("error", reject);
|
|
5228
|
+
server.listen(0, host, () => {
|
|
5229
|
+
const address = server.address();
|
|
5230
|
+
if (!address || typeof address === "string") {
|
|
5231
|
+
server.close(() => reject(new Error("Failed to allocate an internal PM proxy port.")));
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
const port = address.port;
|
|
5235
|
+
server.close((error) => {
|
|
5236
|
+
if (error) {
|
|
5237
|
+
reject(error);
|
|
5238
|
+
return;
|
|
5239
|
+
}
|
|
5240
|
+
resolve7(port);
|
|
5241
|
+
});
|
|
5242
|
+
});
|
|
5243
|
+
});
|
|
5244
|
+
}
|
|
5245
|
+
function buildPmProxyHeaders(headersInput, host) {
|
|
5246
|
+
const headers = {};
|
|
5247
|
+
for (const [key, value] of Object.entries(headersInput)) {
|
|
5248
|
+
if (value !== void 0) {
|
|
5249
|
+
headers[key] = value;
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
headers.host = host;
|
|
5253
|
+
return headers;
|
|
5254
|
+
}
|
|
5255
|
+
function writeRawHttpResponse(socket, statusCode, statusMessage, headers) {
|
|
5256
|
+
const lines = [`HTTP/1.1 ${statusCode} ${statusMessage}`];
|
|
5257
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
5258
|
+
if (value === void 0) {
|
|
5259
|
+
continue;
|
|
5260
|
+
}
|
|
5261
|
+
if (Array.isArray(value)) {
|
|
5262
|
+
for (const item of value) {
|
|
5263
|
+
lines.push(`${key}: ${item}`);
|
|
5264
|
+
}
|
|
5265
|
+
continue;
|
|
5266
|
+
}
|
|
5267
|
+
lines.push(`${key}: ${value}`);
|
|
5268
|
+
}
|
|
5269
|
+
socket.write(`${lines.join("\r\n")}\r
|
|
5270
|
+
\r
|
|
5271
|
+
`);
|
|
5272
|
+
}
|
|
5273
|
+
async function createPmProxyController(proxy) {
|
|
5274
|
+
let targets = [];
|
|
5275
|
+
let nextTargetIndex = 0;
|
|
5276
|
+
const setResolvedTargets = (nextTargets) => {
|
|
5277
|
+
const unchanged = nextTargets.length === targets.length && nextTargets.every((target, index) => targets[index]?.href === target.href);
|
|
5278
|
+
targets = nextTargets;
|
|
5279
|
+
if (targets.length === 0) {
|
|
5280
|
+
nextTargetIndex = 0;
|
|
5281
|
+
return;
|
|
5282
|
+
}
|
|
5283
|
+
if (!unchanged) {
|
|
5284
|
+
nextTargetIndex = nextTargetIndex % targets.length;
|
|
5285
|
+
}
|
|
5286
|
+
};
|
|
5287
|
+
const pickTarget = () => {
|
|
5288
|
+
if (targets.length === 0) {
|
|
5289
|
+
return null;
|
|
5290
|
+
}
|
|
5291
|
+
const target = targets[nextTargetIndex % targets.length];
|
|
5292
|
+
nextTargetIndex = (nextTargetIndex + 1) % targets.length;
|
|
5293
|
+
return target;
|
|
5294
|
+
};
|
|
5295
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
5296
|
+
const target = pickTarget();
|
|
5297
|
+
if (!target) {
|
|
5298
|
+
res.statusCode = 503;
|
|
5299
|
+
res.end("PM proxy target is not ready.");
|
|
5300
|
+
return;
|
|
5301
|
+
}
|
|
5302
|
+
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5303
|
+
const targetUrl = new URL(req.url || "/", target);
|
|
5304
|
+
const headers = buildPmProxyHeaders(req.headers, target.host);
|
|
5305
|
+
const proxyReq = requestLib(targetUrl, {
|
|
5306
|
+
method: req.method,
|
|
5307
|
+
headers
|
|
5308
|
+
}, (proxyRes) => {
|
|
5309
|
+
const outgoingHeaders = {};
|
|
5310
|
+
for (const [key, value] of Object.entries(proxyRes.headers)) {
|
|
5311
|
+
if (value !== void 0) {
|
|
5312
|
+
outgoingHeaders[key] = value;
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
res.writeHead(proxyRes.statusCode || 200, outgoingHeaders);
|
|
5316
|
+
proxyRes.pipe(res);
|
|
5317
|
+
});
|
|
5318
|
+
proxyReq.on("error", (error) => {
|
|
5319
|
+
if (!res.headersSent) {
|
|
5320
|
+
res.statusCode = 502;
|
|
5321
|
+
}
|
|
5322
|
+
res.end(`PM proxy error: ${error.message}`);
|
|
5323
|
+
});
|
|
5324
|
+
req.pipe(proxyReq);
|
|
5325
|
+
});
|
|
5326
|
+
server.on("upgrade", (req, socket, head) => {
|
|
5327
|
+
const target = pickTarget();
|
|
5328
|
+
if (!target) {
|
|
5329
|
+
writeRawHttpResponse(socket, 503, "Service Unavailable", {
|
|
5330
|
+
connection: "close",
|
|
5331
|
+
"content-length": 0
|
|
5332
|
+
});
|
|
5333
|
+
socket.destroy();
|
|
5334
|
+
return;
|
|
5335
|
+
}
|
|
5336
|
+
const requestLib = target.protocol === "https:" ? import_node_https.request : import_node_http.request;
|
|
5337
|
+
const targetUrl = new URL(req.url || "/", target);
|
|
5338
|
+
const proxyReq = requestLib(targetUrl, {
|
|
5339
|
+
method: req.method,
|
|
5340
|
+
headers: buildPmProxyHeaders(req.headers, target.host)
|
|
5341
|
+
});
|
|
5342
|
+
proxyReq.on("upgrade", (proxyRes, proxySocket, proxyHead) => {
|
|
5343
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 101, proxyRes.statusMessage || "Switching Protocols", proxyRes.headers);
|
|
5344
|
+
if (head.length > 0) {
|
|
5345
|
+
proxySocket.write(head);
|
|
5346
|
+
}
|
|
5347
|
+
if (proxyHead.length > 0) {
|
|
5348
|
+
socket.write(proxyHead);
|
|
5349
|
+
}
|
|
5350
|
+
socket.on("error", () => proxySocket.destroy());
|
|
5351
|
+
proxySocket.on("error", () => socket.destroy());
|
|
5352
|
+
proxySocket.pipe(socket);
|
|
5353
|
+
socket.pipe(proxySocket);
|
|
5354
|
+
});
|
|
5355
|
+
proxyReq.on("response", (proxyRes) => {
|
|
5356
|
+
writeRawHttpResponse(socket, proxyRes.statusCode || 502, proxyRes.statusMessage || "Bad Gateway", proxyRes.headers);
|
|
5357
|
+
proxyRes.pipe(socket);
|
|
5358
|
+
});
|
|
5359
|
+
proxyReq.on("error", (error) => {
|
|
5360
|
+
writeRawHttpResponse(socket, 502, "Bad Gateway", {
|
|
5361
|
+
connection: "close",
|
|
5362
|
+
"content-type": "text/plain; charset=utf-8",
|
|
5363
|
+
"content-length": Buffer.byteLength(`PM proxy error: ${error.message}`)
|
|
5364
|
+
});
|
|
5365
|
+
socket.end(`PM proxy error: ${error.message}`);
|
|
5366
|
+
});
|
|
5367
|
+
proxyReq.end();
|
|
5368
|
+
});
|
|
5369
|
+
await new Promise((resolve7, reject) => {
|
|
5370
|
+
server.once("error", reject);
|
|
5371
|
+
server.listen(proxy.port, resolvePmProxyHost(proxy), () => resolve7());
|
|
5372
|
+
});
|
|
5373
|
+
return {
|
|
5374
|
+
setTarget(targetUrl) {
|
|
5375
|
+
setResolvedTargets(targetUrl ? [new URL(targetUrl)] : []);
|
|
5376
|
+
},
|
|
5377
|
+
setTargets(targetUrls) {
|
|
5378
|
+
setResolvedTargets(targetUrls.map((targetUrl) => new URL(targetUrl)));
|
|
5379
|
+
},
|
|
5380
|
+
close() {
|
|
5381
|
+
return new Promise((resolve7, reject) => {
|
|
5382
|
+
server.close((error) => {
|
|
5383
|
+
if (error) {
|
|
5384
|
+
reject(error);
|
|
5385
|
+
return;
|
|
5386
|
+
}
|
|
5387
|
+
resolve7();
|
|
5388
|
+
});
|
|
5389
|
+
});
|
|
5390
|
+
}
|
|
5391
|
+
};
|
|
5392
|
+
}
|
|
5393
|
+
|
|
4658
5394
|
// src/cli/pm/runner.ts
|
|
4659
5395
|
function writePmLog(stream, message) {
|
|
4660
|
-
stream.
|
|
5396
|
+
if (stream.writableEnded || stream.destroyed) {
|
|
5397
|
+
return;
|
|
5398
|
+
}
|
|
5399
|
+
try {
|
|
5400
|
+
stream.write(`[elit pm] ${(/* @__PURE__ */ new Date()).toISOString()} ${message}${import_node_os.EOL}`);
|
|
5401
|
+
} catch (error) {
|
|
5402
|
+
if (error.code !== "ERR_STREAM_WRITE_AFTER_END") {
|
|
5403
|
+
throw error;
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
4661
5406
|
}
|
|
4662
5407
|
function waitForExit(code, signal) {
|
|
4663
5408
|
if (typeof code === "number") {
|
|
@@ -4671,38 +5416,367 @@ error: ${text}`);
|
|
|
4671
5416
|
async function delay(milliseconds) {
|
|
4672
5417
|
await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
4673
5418
|
}
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
await delay(DEFAULT_PM_STOP_POLL_MS);
|
|
4684
|
-
}
|
|
4685
|
-
return !isProcessAlive(pid);
|
|
5419
|
+
function resolveRunnerPathsFromRecordFile(filePath) {
|
|
5420
|
+
const appsDir = (0, import_node_path6.dirname)(filePath);
|
|
5421
|
+
const dataDir = (0, import_node_path6.dirname)(appsDir);
|
|
5422
|
+
return {
|
|
5423
|
+
dataDir,
|
|
5424
|
+
appsDir,
|
|
5425
|
+
logsDir: (0, import_node_path6.join)(dataDir, "logs"),
|
|
5426
|
+
dumpFile: (0, import_node_path6.join)(dataDir, DEFAULT_PM_DUMP_FILE)
|
|
5427
|
+
};
|
|
4686
5428
|
}
|
|
4687
|
-
|
|
4688
|
-
return
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
5429
|
+
function usesPmProxyController(record) {
|
|
5430
|
+
return Boolean(record.proxy) && (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "proxy";
|
|
5431
|
+
}
|
|
5432
|
+
function usesPmInheritedListener(record) {
|
|
5433
|
+
return Boolean(record.proxy) && (record.proxy?.strategy ?? DEFAULT_PM_PROXY_STRATEGY) === "inherit";
|
|
5434
|
+
}
|
|
5435
|
+
function isPmProxyOwner(record) {
|
|
5436
|
+
return usesPmProxyController(record) && record.instanceIndex === 1;
|
|
5437
|
+
}
|
|
5438
|
+
function resolvePmProxyTargetUrls(paths, baseName) {
|
|
5439
|
+
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));
|
|
5440
|
+
}
|
|
5441
|
+
async function createPmInheritedListener(proxy) {
|
|
5442
|
+
const server = (0, import_node_http2.createServer)();
|
|
5443
|
+
await new Promise((resolvePromise, reject) => {
|
|
5444
|
+
server.once("error", reject);
|
|
5445
|
+
server.listen(proxy.port, resolvePmProxyHost(proxy), () => resolvePromise());
|
|
5446
|
+
});
|
|
5447
|
+
return server;
|
|
5448
|
+
}
|
|
5449
|
+
function createPmChildIpcState(child, sharedListener) {
|
|
5450
|
+
let bootstrapReady = false;
|
|
5451
|
+
let listenerReady = false;
|
|
5452
|
+
let sharedHandleSent = false;
|
|
5453
|
+
const sendSharedHandle = () => {
|
|
5454
|
+
if (!sharedListener || !bootstrapReady || sharedHandleSent || !child.connected) {
|
|
5455
|
+
return;
|
|
5456
|
+
}
|
|
5457
|
+
sharedHandleSent = true;
|
|
5458
|
+
child.send?.({ type: "elit:pm:listen-handle" }, sharedListener);
|
|
5459
|
+
};
|
|
5460
|
+
const onMessage = (message) => {
|
|
5461
|
+
if (!message || typeof message !== "object") {
|
|
5462
|
+
return;
|
|
5463
|
+
}
|
|
5464
|
+
if (message.type === "elit:pm:bootstrap-ready") {
|
|
5465
|
+
bootstrapReady = true;
|
|
5466
|
+
sendSharedHandle();
|
|
5467
|
+
return;
|
|
5468
|
+
}
|
|
5469
|
+
if (message.type === "elit:pm:listener-ready") {
|
|
5470
|
+
listenerReady = true;
|
|
5471
|
+
}
|
|
5472
|
+
};
|
|
5473
|
+
child.on("message", onMessage);
|
|
5474
|
+
return {
|
|
5475
|
+
get bootstrapReady() {
|
|
5476
|
+
return bootstrapReady;
|
|
5477
|
+
},
|
|
5478
|
+
get listenerReady() {
|
|
5479
|
+
return listenerReady;
|
|
5480
|
+
},
|
|
5481
|
+
stop() {
|
|
5482
|
+
child.off("message", onMessage);
|
|
5483
|
+
}
|
|
5484
|
+
};
|
|
5485
|
+
}
|
|
5486
|
+
function buildPmChildEnv(record, command, targetPort) {
|
|
5487
|
+
return {
|
|
5488
|
+
...process.env,
|
|
5489
|
+
...record.env,
|
|
5490
|
+
...command.env,
|
|
5491
|
+
...usesPmProxyController(record) && targetPort ? {
|
|
5492
|
+
[resolvePmProxyEnvVar(record.proxy)]: String(targetPort),
|
|
5493
|
+
ELIT_PM_PUBLIC_PORT: String(record.proxy.port)
|
|
5494
|
+
} : {},
|
|
5495
|
+
ELIT_PM_NAME: record.name,
|
|
5496
|
+
ELIT_PM_ID: record.id
|
|
5497
|
+
};
|
|
5498
|
+
}
|
|
5499
|
+
function buildPmChildStdio(command, onlineStdinShutdownEnabled) {
|
|
5500
|
+
return [
|
|
5501
|
+
onlineStdinShutdownEnabled ? "pipe" : "ignore",
|
|
5502
|
+
"pipe",
|
|
5503
|
+
"pipe",
|
|
5504
|
+
...command.ipc ? ["ipc"] : []
|
|
5505
|
+
];
|
|
5506
|
+
}
|
|
5507
|
+
function createPmReadinessMonitor(record, onReady, onFailure, options) {
|
|
5508
|
+
if (options?.ipcController) {
|
|
5509
|
+
let stopped2 = false;
|
|
5510
|
+
let timer2 = null;
|
|
5511
|
+
let timeoutTimer2 = null;
|
|
5512
|
+
const clearTimers2 = () => {
|
|
5513
|
+
if (timer2) {
|
|
5514
|
+
clearInterval(timer2);
|
|
5515
|
+
timer2 = null;
|
|
5516
|
+
}
|
|
5517
|
+
if (timeoutTimer2) {
|
|
5518
|
+
clearTimeout(timeoutTimer2);
|
|
5519
|
+
timeoutTimer2 = null;
|
|
5520
|
+
}
|
|
5521
|
+
};
|
|
5522
|
+
const host = record.proxy?.host ?? "0.0.0.0";
|
|
5523
|
+
const port = record.proxy?.port ?? 0;
|
|
5524
|
+
timer2 = setInterval(() => {
|
|
5525
|
+
if (stopped2 || !options.ipcController?.listenerReady) {
|
|
5526
|
+
return;
|
|
5527
|
+
}
|
|
5528
|
+
stopped2 = true;
|
|
5529
|
+
clearTimers2();
|
|
5530
|
+
onReady(`shared listener ready on ${host}:${port}`);
|
|
5531
|
+
}, 25);
|
|
5532
|
+
timer2.unref?.();
|
|
5533
|
+
timeoutTimer2 = setTimeout(() => {
|
|
5534
|
+
if (stopped2) {
|
|
5535
|
+
return;
|
|
5536
|
+
}
|
|
5537
|
+
stopped2 = true;
|
|
5538
|
+
clearTimers2();
|
|
5539
|
+
onFailure(`listen timeout reached after ${record.listenTimeout}ms while waiting for shared listener ${host}:${port}`);
|
|
5540
|
+
}, record.listenTimeout);
|
|
5541
|
+
timeoutTimer2.unref?.();
|
|
5542
|
+
return {
|
|
5543
|
+
stop() {
|
|
5544
|
+
stopped2 = true;
|
|
5545
|
+
clearTimers2();
|
|
5546
|
+
}
|
|
5547
|
+
};
|
|
5548
|
+
}
|
|
5549
|
+
if (!record.waitReady || !record.healthCheck) {
|
|
5550
|
+
return {
|
|
5551
|
+
stop() {
|
|
5552
|
+
}
|
|
5553
|
+
};
|
|
5554
|
+
}
|
|
5555
|
+
const healthCheck = record.healthCheck;
|
|
5556
|
+
const pollInterval = Math.max(50, Math.min(healthCheck.interval, 250));
|
|
5557
|
+
let stopped = false;
|
|
5558
|
+
let timer = null;
|
|
5559
|
+
let timeoutTimer = null;
|
|
5560
|
+
let inFlight = false;
|
|
5561
|
+
const clearTimers = () => {
|
|
5562
|
+
if (timer) {
|
|
5563
|
+
clearInterval(timer);
|
|
5564
|
+
timer = null;
|
|
5565
|
+
}
|
|
5566
|
+
if (timeoutTimer) {
|
|
5567
|
+
clearTimeout(timeoutTimer);
|
|
5568
|
+
timeoutTimer = null;
|
|
5569
|
+
}
|
|
5570
|
+
};
|
|
5571
|
+
const runHealthCheck = async () => {
|
|
5572
|
+
if (stopped || inFlight) {
|
|
5573
|
+
return;
|
|
5574
|
+
}
|
|
5575
|
+
inFlight = true;
|
|
5576
|
+
const controller = new AbortController();
|
|
5577
|
+
const timeoutId = setTimeout(() => controller.abort(), healthCheck.timeout);
|
|
5578
|
+
timeoutId.unref?.();
|
|
5579
|
+
try {
|
|
5580
|
+
const response = await fetch(healthCheck.url, {
|
|
5581
|
+
method: "GET",
|
|
5582
|
+
signal: controller.signal
|
|
5583
|
+
});
|
|
5584
|
+
if (stopped) {
|
|
5585
|
+
return;
|
|
5586
|
+
}
|
|
5587
|
+
if (!response.ok) {
|
|
5588
|
+
throw new Error(`health check returned ${response.status}`);
|
|
5589
|
+
}
|
|
5590
|
+
stopped = true;
|
|
5591
|
+
clearTimers();
|
|
5592
|
+
onReady(`readiness check passed: ${healthCheck.url}`);
|
|
5593
|
+
} catch {
|
|
5594
|
+
} finally {
|
|
5595
|
+
clearTimeout(timeoutId);
|
|
5596
|
+
inFlight = false;
|
|
5597
|
+
}
|
|
5598
|
+
};
|
|
5599
|
+
timeoutTimer = setTimeout(() => {
|
|
5600
|
+
if (stopped) {
|
|
5601
|
+
return;
|
|
5602
|
+
}
|
|
5603
|
+
stopped = true;
|
|
5604
|
+
clearTimers();
|
|
5605
|
+
onFailure(`listen timeout reached after ${record.listenTimeout}ms while waiting for ${healthCheck.url}`);
|
|
5606
|
+
}, record.listenTimeout);
|
|
5607
|
+
timeoutTimer.unref?.();
|
|
5608
|
+
void runHealthCheck();
|
|
5609
|
+
timer = setInterval(() => {
|
|
5610
|
+
void runHealthCheck();
|
|
5611
|
+
}, pollInterval);
|
|
5612
|
+
timer.unref?.();
|
|
5613
|
+
return {
|
|
5614
|
+
stop() {
|
|
5615
|
+
stopped = true;
|
|
5616
|
+
clearTimers();
|
|
5617
|
+
}
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5620
|
+
function resolvePmStopTimeout(record) {
|
|
5621
|
+
if (isPmOnlineWapkRecord(record)) {
|
|
5622
|
+
return Math.max(record.killTimeout, PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS);
|
|
5623
|
+
}
|
|
5624
|
+
return record.killTimeout;
|
|
5625
|
+
}
|
|
5626
|
+
function supportsPmProxyReload(record) {
|
|
5627
|
+
return Boolean(record.proxy) && record.instances === 1;
|
|
5628
|
+
}
|
|
5629
|
+
function buildPmMonitorRecord(record, targetPort) {
|
|
5630
|
+
if (!record.proxy || !targetPort) {
|
|
5631
|
+
return record;
|
|
5632
|
+
}
|
|
5633
|
+
const targetHost = resolvePmProxyTargetHost(record.proxy);
|
|
5634
|
+
return {
|
|
5635
|
+
...record,
|
|
5636
|
+
proxyTargetPort: targetPort,
|
|
5637
|
+
healthCheck: record.healthCheck ? {
|
|
5638
|
+
...record.healthCheck,
|
|
5639
|
+
url: rewritePmProxyHealthCheckUrl(record.healthCheck.url, targetHost, targetPort)
|
|
5640
|
+
} : void 0
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
5643
|
+
async function waitForProcessTermination(pid, timeoutMs) {
|
|
5644
|
+
if (!pid || !isProcessAlive(pid)) {
|
|
5645
|
+
return true;
|
|
5646
|
+
}
|
|
5647
|
+
const deadline = Date.now() + timeoutMs;
|
|
5648
|
+
while (Date.now() < deadline) {
|
|
5649
|
+
if (!isProcessAlive(pid)) {
|
|
5650
|
+
return true;
|
|
5651
|
+
}
|
|
5652
|
+
await delay(DEFAULT_PM_STOP_POLL_MS);
|
|
5653
|
+
}
|
|
5654
|
+
return !isProcessAlive(pid);
|
|
5655
|
+
}
|
|
5656
|
+
async function waitForManagedChildExit(child) {
|
|
5657
|
+
return await new Promise((resolvePromise) => {
|
|
5658
|
+
let resolved = false;
|
|
5659
|
+
child.once("error", (error) => {
|
|
5660
|
+
if (resolved) {
|
|
5661
|
+
return;
|
|
5662
|
+
}
|
|
5663
|
+
resolved = true;
|
|
5664
|
+
resolvePromise({ code: 1, signal: null, error: error instanceof Error ? error.message : String(error) });
|
|
5665
|
+
});
|
|
5666
|
+
child.once("close", (code, signal) => {
|
|
5667
|
+
if (resolved) {
|
|
5668
|
+
return;
|
|
5669
|
+
}
|
|
5670
|
+
resolved = true;
|
|
5671
|
+
resolvePromise({ code, signal });
|
|
4703
5672
|
});
|
|
4704
5673
|
});
|
|
4705
5674
|
}
|
|
5675
|
+
async function createPmChildControllers(record, child, stdoutLog, stderrLog, requestPlannedRestart, onReady, options) {
|
|
5676
|
+
let healthMonitor = {
|
|
5677
|
+
stop() {
|
|
5678
|
+
}
|
|
5679
|
+
};
|
|
5680
|
+
let memoryMonitor = {
|
|
5681
|
+
stop() {
|
|
5682
|
+
}
|
|
5683
|
+
};
|
|
5684
|
+
let scheduleMonitor = {
|
|
5685
|
+
stop() {
|
|
5686
|
+
}
|
|
5687
|
+
};
|
|
5688
|
+
const startHealthMonitor = () => {
|
|
5689
|
+
healthMonitor = createPmHealthMonitor(
|
|
5690
|
+
record,
|
|
5691
|
+
(message) => requestPlannedRestart("health", message),
|
|
5692
|
+
(message) => writePmLog(stdoutLog, message)
|
|
5693
|
+
);
|
|
5694
|
+
};
|
|
5695
|
+
const needsReadySignal = Boolean(options?.ipcController) || record.waitReady;
|
|
5696
|
+
const readinessMonitor = options?.ready || !needsReadySignal ? { stop() {
|
|
5697
|
+
} } : createPmReadinessMonitor(
|
|
5698
|
+
record,
|
|
5699
|
+
(message) => {
|
|
5700
|
+
onReady(message);
|
|
5701
|
+
startHealthMonitor();
|
|
5702
|
+
},
|
|
5703
|
+
(message) => requestPlannedRestart("startup", message),
|
|
5704
|
+
{ ipcController: options?.ipcController }
|
|
5705
|
+
);
|
|
5706
|
+
const watchController = await createPmWatchController(
|
|
5707
|
+
record,
|
|
5708
|
+
(changedPath) => requestPlannedRestart("watch", changedPath),
|
|
5709
|
+
(message) => writePmLog(stderrLog, `watch error: ${message}`)
|
|
5710
|
+
);
|
|
5711
|
+
memoryMonitor = createPmMemoryMonitor(
|
|
5712
|
+
record,
|
|
5713
|
+
child.pid,
|
|
5714
|
+
(kind, message) => requestPlannedRestart(kind, message)
|
|
5715
|
+
);
|
|
5716
|
+
scheduleMonitor = createPmScheduleMonitor(
|
|
5717
|
+
record,
|
|
5718
|
+
(message) => requestPlannedRestart("cron", message),
|
|
5719
|
+
(message) => writePmLog(stdoutLog, message)
|
|
5720
|
+
);
|
|
5721
|
+
if (options?.ready || !needsReadySignal) {
|
|
5722
|
+
startHealthMonitor();
|
|
5723
|
+
}
|
|
5724
|
+
return {
|
|
5725
|
+
async stop() {
|
|
5726
|
+
await watchController.close();
|
|
5727
|
+
readinessMonitor.stop();
|
|
5728
|
+
healthMonitor.stop();
|
|
5729
|
+
memoryMonitor.stop();
|
|
5730
|
+
scheduleMonitor.stop();
|
|
5731
|
+
}
|
|
5732
|
+
};
|
|
5733
|
+
}
|
|
5734
|
+
async function waitForPmChildReady(record, child, ipcController) {
|
|
5735
|
+
if (!ipcController && (!record.waitReady || !record.healthCheck)) {
|
|
5736
|
+
return { ready: true };
|
|
5737
|
+
}
|
|
5738
|
+
let readyMessage;
|
|
5739
|
+
let failureMessage;
|
|
5740
|
+
let exitResult;
|
|
5741
|
+
const readinessMonitor = createPmReadinessMonitor(
|
|
5742
|
+
record,
|
|
5743
|
+
(message) => {
|
|
5744
|
+
readyMessage = message;
|
|
5745
|
+
},
|
|
5746
|
+
(message) => {
|
|
5747
|
+
failureMessage = message;
|
|
5748
|
+
},
|
|
5749
|
+
{ ipcController }
|
|
5750
|
+
);
|
|
5751
|
+
void waitForManagedChildExit(child).then((result) => {
|
|
5752
|
+
exitResult = result;
|
|
5753
|
+
});
|
|
5754
|
+
while (!readyMessage && !failureMessage && !exitResult) {
|
|
5755
|
+
await delay(25);
|
|
5756
|
+
}
|
|
5757
|
+
readinessMonitor.stop();
|
|
5758
|
+
if (readyMessage) {
|
|
5759
|
+
return { ready: true, message: readyMessage };
|
|
5760
|
+
}
|
|
5761
|
+
return {
|
|
5762
|
+
ready: false,
|
|
5763
|
+
message: failureMessage,
|
|
5764
|
+
exitResult
|
|
5765
|
+
};
|
|
5766
|
+
}
|
|
5767
|
+
async function stopProxyManagedChild(child, record, stderrLog) {
|
|
5768
|
+
if (!child.pid || !isProcessAlive(child.pid)) {
|
|
5769
|
+
return;
|
|
5770
|
+
}
|
|
5771
|
+
terminateProcessTree(child.pid);
|
|
5772
|
+
const stopTimeout = resolvePmStopTimeout(record);
|
|
5773
|
+
const stopped = await waitForProcessTermination(child.pid, stopTimeout);
|
|
5774
|
+
if (!stopped && child.pid && isProcessAlive(child.pid)) {
|
|
5775
|
+
writePmLog(stderrLog, `proxy handoff shutdown timed out after ${stopTimeout}ms; forcing process termination`);
|
|
5776
|
+
terminateProcessTree(child.pid, { force: true });
|
|
5777
|
+
await waitForProcessTermination(child.pid, DEFAULT_PM_STOP_POLL_MS);
|
|
5778
|
+
}
|
|
5779
|
+
}
|
|
4706
5780
|
async function createPmWatchController(record, onChange, onError) {
|
|
4707
5781
|
if (!record.watch || record.watchPaths.length === 0) {
|
|
4708
5782
|
return {
|
|
@@ -4766,11 +5840,17 @@ error: ${text}`);
|
|
|
4766
5840
|
method: "GET",
|
|
4767
5841
|
signal: controller.signal
|
|
4768
5842
|
});
|
|
5843
|
+
if (stopped) {
|
|
5844
|
+
return;
|
|
5845
|
+
}
|
|
4769
5846
|
if (!response.ok) {
|
|
4770
5847
|
throw new Error(`health check returned ${response.status}`);
|
|
4771
5848
|
}
|
|
4772
5849
|
failureCount = 0;
|
|
4773
5850
|
} catch (error) {
|
|
5851
|
+
if (stopped) {
|
|
5852
|
+
return;
|
|
5853
|
+
}
|
|
4774
5854
|
failureCount += 1;
|
|
4775
5855
|
const message = error instanceof Error ? error.message : String(error);
|
|
4776
5856
|
onLog(`health check failed (${failureCount}/${healthCheck.maxFailures}): ${message}`);
|
|
@@ -4805,6 +5885,90 @@ error: ${text}`);
|
|
|
4805
5885
|
}
|
|
4806
5886
|
};
|
|
4807
5887
|
}
|
|
5888
|
+
function createPmMemoryMonitor(record, pid, onFailure) {
|
|
5889
|
+
if (!record.maxMemoryBytes || !pid) {
|
|
5890
|
+
return {
|
|
5891
|
+
stop() {
|
|
5892
|
+
}
|
|
5893
|
+
};
|
|
5894
|
+
}
|
|
5895
|
+
const memoryLimit = record.maxMemoryBytes;
|
|
5896
|
+
let stopped = false;
|
|
5897
|
+
const timer = setInterval(() => {
|
|
5898
|
+
if (stopped) {
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
const memoryRssBytes = samplePmProcessMetrics(pid).memoryRssBytes;
|
|
5902
|
+
if (memoryRssBytes === void 0 || memoryRssBytes <= memoryLimit) {
|
|
5903
|
+
return;
|
|
5904
|
+
}
|
|
5905
|
+
stopped = true;
|
|
5906
|
+
onFailure(record.memoryAction === "stop" ? "memory-stop" : "memory", `memory usage ${memoryRssBytes} exceeded limit ${memoryLimit}`);
|
|
5907
|
+
}, DEFAULT_PM_MEMORY_CHECK_INTERVAL);
|
|
5908
|
+
timer.unref?.();
|
|
5909
|
+
return {
|
|
5910
|
+
stop() {
|
|
5911
|
+
stopped = true;
|
|
5912
|
+
clearInterval(timer);
|
|
5913
|
+
}
|
|
5914
|
+
};
|
|
5915
|
+
}
|
|
5916
|
+
function createPmScheduleMonitor(record, onTrigger, onLog) {
|
|
5917
|
+
if (!record.cronRestart) {
|
|
5918
|
+
return {
|
|
5919
|
+
stop() {
|
|
5920
|
+
}
|
|
5921
|
+
};
|
|
5922
|
+
}
|
|
5923
|
+
const schedule = parsePmRestartSchedule(record.cronRestart, "pm cronRestart");
|
|
5924
|
+
let stopped = false;
|
|
5925
|
+
let timer = null;
|
|
5926
|
+
const armTimer = (from = /* @__PURE__ */ new Date()) => {
|
|
5927
|
+
const nextOccurrence = resolveNextPmScheduleOccurrence(schedule, from);
|
|
5928
|
+
if (!nextOccurrence) {
|
|
5929
|
+
onLog(`schedule has no next occurrence: ${record.cronRestart}`);
|
|
5930
|
+
return;
|
|
5931
|
+
}
|
|
5932
|
+
const delayMs = Math.max(0, nextOccurrence.getTime() - Date.now());
|
|
5933
|
+
timer = setTimeout(() => {
|
|
5934
|
+
timer = null;
|
|
5935
|
+
if (stopped) {
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
5938
|
+
onTrigger(`restart schedule matched: ${record.cronRestart}`);
|
|
5939
|
+
}, delayMs);
|
|
5940
|
+
timer.unref?.();
|
|
5941
|
+
};
|
|
5942
|
+
armTimer();
|
|
5943
|
+
return {
|
|
5944
|
+
stop() {
|
|
5945
|
+
stopped = true;
|
|
5946
|
+
if (timer) {
|
|
5947
|
+
clearTimeout(timer);
|
|
5948
|
+
timer = null;
|
|
5949
|
+
}
|
|
5950
|
+
}
|
|
5951
|
+
};
|
|
5952
|
+
}
|
|
5953
|
+
function resolvePmRestartDelay(record, restartCount, shouldApplyBackoff) {
|
|
5954
|
+
if (!shouldApplyBackoff || !record.expBackoffRestartDelay) {
|
|
5955
|
+
return record.restartDelay;
|
|
5956
|
+
}
|
|
5957
|
+
const exponent = Math.max(0, restartCount - 1);
|
|
5958
|
+
return Math.min(record.expBackoffRestartDelay * 2 ** exponent, record.expBackoffRestartMaxDelay ?? DEFAULT_PM_EXP_BACKOFF_MAX_DELAY);
|
|
5959
|
+
}
|
|
5960
|
+
function resolvePmRestartCountBase(record, wasStable, restartKind) {
|
|
5961
|
+
if (wasStable) {
|
|
5962
|
+
return 0;
|
|
5963
|
+
}
|
|
5964
|
+
if (record.restartWindow && record.lastRestartAt) {
|
|
5965
|
+
const lastRestartTime = Date.parse(record.lastRestartAt);
|
|
5966
|
+
if (!Number.isNaN(lastRestartTime) && Date.now() - lastRestartTime > record.restartWindow) {
|
|
5967
|
+
return 0;
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
return restartKind === "watch" ? record.restartCount ?? 0 : record.restartCount ?? 0;
|
|
5971
|
+
}
|
|
4808
5972
|
function readPlannedRestartRequest(state) {
|
|
4809
5973
|
return state.request;
|
|
4810
5974
|
}
|
|
@@ -4818,6 +5982,16 @@ error: ${text}`);
|
|
|
4818
5982
|
(0, import_node_fs4.mkdirSync)((0, import_node_path6.dirname)(initialRecord.logFiles.err), { recursive: true });
|
|
4819
5983
|
const stdoutLog = (0, import_node_fs4.createWriteStream)(initialRecord.logFiles.out, { flags: "a" });
|
|
4820
5984
|
const stderrLog = (0, import_node_fs4.createWriteStream)(initialRecord.logFiles.err, { flags: "a" });
|
|
5985
|
+
let proxyController = null;
|
|
5986
|
+
let proxyTargetSyncTimer = null;
|
|
5987
|
+
let inheritedListener = null;
|
|
5988
|
+
const runnerPaths = resolveRunnerPathsFromRecordFile(filePath);
|
|
5989
|
+
const syncOwnedProxyTargets = (baseName) => {
|
|
5990
|
+
if (!proxyController) {
|
|
5991
|
+
return;
|
|
5992
|
+
}
|
|
5993
|
+
proxyController.setTargets(resolvePmProxyTargetUrls(runnerPaths, baseName));
|
|
5994
|
+
};
|
|
4821
5995
|
const persist = (mutator) => {
|
|
4822
5996
|
const current = readLatestPmRecord(filePath, record);
|
|
4823
5997
|
record = mutator(current);
|
|
@@ -4830,26 +6004,30 @@ error: ${text}`);
|
|
|
4830
6004
|
activeChildStopTimer = null;
|
|
4831
6005
|
}
|
|
4832
6006
|
};
|
|
6007
|
+
const scheduleForcedActiveChildStop = (timeoutMs, reason) => {
|
|
6008
|
+
if (!activeChild?.pid || process.platform === "win32") {
|
|
6009
|
+
return;
|
|
6010
|
+
}
|
|
6011
|
+
clearActiveChildStopTimer();
|
|
6012
|
+
activeChildStopTimer = setTimeout(() => {
|
|
6013
|
+
if (activeChild?.pid && isProcessAlive(activeChild.pid)) {
|
|
6014
|
+
writePmLog(stderrLog, `${reason} after ${timeoutMs}ms; forcing process termination`);
|
|
6015
|
+
terminateProcessTree(activeChild.pid, { force: true });
|
|
6016
|
+
}
|
|
6017
|
+
}, timeoutMs);
|
|
6018
|
+
activeChildStopTimer.unref?.();
|
|
6019
|
+
};
|
|
4833
6020
|
const stopActiveChild = () => {
|
|
4834
6021
|
if (!activeChild?.pid || !isProcessAlive(activeChild.pid)) {
|
|
4835
6022
|
return;
|
|
4836
6023
|
}
|
|
4837
6024
|
const current = readLatestPmRecord(filePath, record);
|
|
6025
|
+
const stopTimeout = resolvePmStopTimeout(current);
|
|
4838
6026
|
if (isPmOnlineWapkRecord(current) && activeChild.stdin && !activeChild.stdin.destroyed && activeChild.stdin.writable) {
|
|
4839
6027
|
try {
|
|
4840
6028
|
activeChild.stdin.end(`${PM_WAPK_ONLINE_SHUTDOWN_COMMAND}
|
|
4841
6029
|
`);
|
|
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?.();
|
|
6030
|
+
scheduleForcedActiveChildStop(stopTimeout, "graceful WAPK online shutdown timed out");
|
|
4853
6031
|
return;
|
|
4854
6032
|
} catch (error) {
|
|
4855
6033
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -4857,6 +6035,7 @@ error: ${text}`);
|
|
|
4857
6035
|
}
|
|
4858
6036
|
}
|
|
4859
6037
|
terminateProcessTree(activeChild.pid);
|
|
6038
|
+
scheduleForcedActiveChildStop(stopTimeout, "graceful shutdown timed out");
|
|
4860
6039
|
};
|
|
4861
6040
|
const requestManagedStop = (reason) => {
|
|
4862
6041
|
if (stopRequested) {
|
|
@@ -4894,6 +6073,17 @@ error: ${text}`);
|
|
|
4894
6073
|
let command;
|
|
4895
6074
|
try {
|
|
4896
6075
|
command = buildPmCommand(latest);
|
|
6076
|
+
if (latest.proxy && isPmProxyOwner(latest) && !proxyController) {
|
|
6077
|
+
proxyController = await createPmProxyController(latest.proxy);
|
|
6078
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6079
|
+
if (!proxyTargetSyncTimer) {
|
|
6080
|
+
proxyTargetSyncTimer = setInterval(() => syncOwnedProxyTargets(latest.baseName), 50);
|
|
6081
|
+
proxyTargetSyncTimer.unref?.();
|
|
6082
|
+
}
|
|
6083
|
+
}
|
|
6084
|
+
if (latest.proxy && usesPmInheritedListener(latest) && !inheritedListener) {
|
|
6085
|
+
inheritedListener = await createPmInheritedListener(latest.proxy);
|
|
6086
|
+
}
|
|
4897
6087
|
} catch (error) {
|
|
4898
6088
|
const message = error instanceof Error ? error.message : String(error);
|
|
4899
6089
|
writePmLog(stderrLog, message);
|
|
@@ -4903,25 +6093,24 @@ error: ${text}`);
|
|
|
4903
6093
|
error: message,
|
|
4904
6094
|
runnerPid: void 0,
|
|
4905
6095
|
childPid: void 0,
|
|
6096
|
+
proxyTargetPort: void 0,
|
|
6097
|
+
proxyReadyAt: void 0,
|
|
4906
6098
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4907
6099
|
}));
|
|
4908
6100
|
return;
|
|
4909
6101
|
}
|
|
4910
6102
|
const onlineStdinShutdownEnabled = isPmOnlineWapkRecord(latest);
|
|
4911
|
-
const
|
|
6103
|
+
const initialTargetPort = usesPmProxyController(latest) ? await allocatePmProxyTargetPort(resolvePmProxyTargetHost(latest.proxy)) : void 0;
|
|
6104
|
+
const monitorRecord = buildPmMonitorRecord(latest, initialTargetPort);
|
|
6105
|
+
let child = (0, import_node_child_process2.spawn)(command.command, command.args, {
|
|
4912
6106
|
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"],
|
|
6107
|
+
env: buildPmChildEnv(latest, command, initialTargetPort),
|
|
6108
|
+
stdio: buildPmChildStdio(command, onlineStdinShutdownEnabled),
|
|
4921
6109
|
windowsHide: true,
|
|
4922
6110
|
shell: command.shell
|
|
4923
6111
|
});
|
|
4924
|
-
|
|
6112
|
+
let childStartedAt = Date.now();
|
|
6113
|
+
let childIpcState = command.ipc ? createPmChildIpcState(child, inheritedListener) : void 0;
|
|
4925
6114
|
activeChild = child;
|
|
4926
6115
|
if (child.stdout) {
|
|
4927
6116
|
child.stdout.pipe(stdoutLog, { end: false });
|
|
@@ -4929,26 +6118,38 @@ error: ${text}`);
|
|
|
4929
6118
|
if (child.stderr) {
|
|
4930
6119
|
child.stderr.pipe(stderrLog, { end: false });
|
|
4931
6120
|
}
|
|
6121
|
+
let childWaitState = { settled: false, result: void 0 };
|
|
6122
|
+
void waitForManagedChildExit(child).then((result) => {
|
|
6123
|
+
childWaitState.result = result;
|
|
6124
|
+
childWaitState.settled = true;
|
|
6125
|
+
});
|
|
4932
6126
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6127
|
+
const waitingForReady = latest.waitReady || Boolean(childIpcState);
|
|
4933
6128
|
persist((current2) => ({
|
|
4934
6129
|
...current2,
|
|
4935
|
-
status: "online",
|
|
6130
|
+
status: waitingForReady ? "starting" : "online",
|
|
4936
6131
|
commandPreview: command.preview,
|
|
4937
6132
|
runtime: command.runtime ?? current2.runtime,
|
|
4938
6133
|
runnerPid: process.pid,
|
|
4939
6134
|
childPid: child.pid,
|
|
6135
|
+
proxyTargetPort: initialTargetPort,
|
|
6136
|
+
proxyReadyAt: !waitingForReady && usesPmProxyController(latest) ? startedAt : void 0,
|
|
4940
6137
|
startedAt,
|
|
4941
6138
|
stoppedAt: void 0,
|
|
6139
|
+
reloadRequestedAt: void 0,
|
|
4942
6140
|
error: void 0,
|
|
4943
6141
|
updatedAt: startedAt
|
|
4944
6142
|
}));
|
|
4945
6143
|
writePmLog(stdoutLog, `started ${command.preview}${child.pid ? ` (pid ${child.pid})` : ""}`);
|
|
6144
|
+
if (isPmProxyOwner(latest) && !waitingForReady) {
|
|
6145
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6146
|
+
}
|
|
4946
6147
|
const requestPlannedRestart = (kind, detail) => {
|
|
4947
6148
|
if (stopRequested || restartState.request) {
|
|
4948
6149
|
return;
|
|
4949
6150
|
}
|
|
4950
6151
|
restartState.request = { kind, detail };
|
|
4951
|
-
writePmLog(kind === "
|
|
6152
|
+
writePmLog(kind === "watch" || kind === "cron" ? stdoutLog : stderrLog, `${kind} restart requested: ${detail}`);
|
|
4952
6153
|
persist((current2) => ({
|
|
4953
6154
|
...current2,
|
|
4954
6155
|
status: "restarting",
|
|
@@ -4956,27 +6157,124 @@ error: ${text}`);
|
|
|
4956
6157
|
}));
|
|
4957
6158
|
stopActiveChild();
|
|
4958
6159
|
};
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
6160
|
+
let controllers = await createPmChildControllers(
|
|
6161
|
+
monitorRecord,
|
|
6162
|
+
child,
|
|
6163
|
+
stdoutLog,
|
|
6164
|
+
stderrLog,
|
|
6165
|
+
requestPlannedRestart,
|
|
6166
|
+
(message) => {
|
|
6167
|
+
const readyAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6168
|
+
if (isPmProxyOwner(latest)) {
|
|
6169
|
+
syncOwnedProxyTargets(latest.baseName);
|
|
6170
|
+
}
|
|
6171
|
+
persist((current2) => ({
|
|
6172
|
+
...current2,
|
|
6173
|
+
status: "online",
|
|
6174
|
+
proxyTargetPort: initialTargetPort,
|
|
6175
|
+
proxyReadyAt: readyAt,
|
|
6176
|
+
updatedAt: readyAt
|
|
6177
|
+
}));
|
|
6178
|
+
writePmLog(stdoutLog, message);
|
|
6179
|
+
},
|
|
6180
|
+
{ ready: !waitingForReady, ipcController: childIpcState }
|
|
4968
6181
|
);
|
|
4969
|
-
|
|
6182
|
+
let handledReloadAt = latest.reloadRequestedAt;
|
|
6183
|
+
while (!childWaitState.settled) {
|
|
4970
6184
|
const latestRecord = readLatestPmRecord(filePath, record);
|
|
4971
|
-
if (
|
|
6185
|
+
if (latestRecord.desiredState === "stopped" && !stopRequested) {
|
|
4972
6186
|
requestManagedStop("stop requested by PM control state");
|
|
4973
6187
|
}
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
6188
|
+
const reloadRequestedAt = latestRecord.reloadRequestedAt;
|
|
6189
|
+
if (!stopRequested && supportsPmProxyReload(latestRecord) && reloadRequestedAt && reloadRequestedAt !== handledReloadAt && latestRecord.proxy) {
|
|
6190
|
+
handledReloadAt = reloadRequestedAt;
|
|
6191
|
+
const replacementTargetPort = usesPmProxyController(latestRecord) ? await allocatePmProxyTargetPort(resolvePmProxyTargetHost(latestRecord.proxy)) : void 0;
|
|
6192
|
+
const replacementMonitorRecord = buildPmMonitorRecord(latestRecord, replacementTargetPort);
|
|
6193
|
+
const replacementChild = (0, import_node_child_process2.spawn)(command.command, command.args, {
|
|
6194
|
+
cwd: latestRecord.cwd,
|
|
6195
|
+
env: buildPmChildEnv(latestRecord, command, replacementTargetPort),
|
|
6196
|
+
stdio: buildPmChildStdio(command, onlineStdinShutdownEnabled),
|
|
6197
|
+
windowsHide: true,
|
|
6198
|
+
shell: command.shell
|
|
6199
|
+
});
|
|
6200
|
+
const replacementIpcState = command.ipc ? createPmChildIpcState(replacementChild, inheritedListener) : void 0;
|
|
6201
|
+
if (replacementChild.stdout) {
|
|
6202
|
+
replacementChild.stdout.pipe(stdoutLog, { end: false });
|
|
6203
|
+
}
|
|
6204
|
+
if (replacementChild.stderr) {
|
|
6205
|
+
replacementChild.stderr.pipe(stderrLog, { end: false });
|
|
6206
|
+
}
|
|
6207
|
+
writePmLog(stdoutLog, `starting ${usesPmInheritedListener(latestRecord) ? "shared-listener" : "proxy handoff"} replacement${replacementChild.pid ? ` (pid ${replacementChild.pid})` : ""}`);
|
|
6208
|
+
const readyResult = await waitForPmChildReady(replacementMonitorRecord, replacementChild, replacementIpcState);
|
|
6209
|
+
if (!readyResult.ready) {
|
|
6210
|
+
writePmLog(stderrLog, readyResult.message ?? "replacement exited before becoming ready");
|
|
6211
|
+
replacementIpcState?.stop();
|
|
6212
|
+
await stopProxyManagedChild(replacementChild, latestRecord, stderrLog);
|
|
6213
|
+
persist((current2) => ({
|
|
6214
|
+
...current2,
|
|
6215
|
+
status: "online",
|
|
6216
|
+
proxyReadyAt: current2.proxyReadyAt,
|
|
6217
|
+
reloadRequestedAt: void 0,
|
|
6218
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6219
|
+
}));
|
|
6220
|
+
continue;
|
|
6221
|
+
}
|
|
6222
|
+
const previousChild = child;
|
|
6223
|
+
const previousControllers = controllers;
|
|
6224
|
+
const previousIpcState = childIpcState;
|
|
6225
|
+
const handoffAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6226
|
+
if (usesPmProxyController(latestRecord) && replacementTargetPort) {
|
|
6227
|
+
proxyController?.setTarget(buildPmProxyTargetUrl(latestRecord.proxy, replacementTargetPort));
|
|
6228
|
+
}
|
|
6229
|
+
persist((current2) => ({
|
|
6230
|
+
...current2,
|
|
6231
|
+
status: "online",
|
|
6232
|
+
childPid: replacementChild.pid,
|
|
6233
|
+
proxyTargetPort: replacementTargetPort,
|
|
6234
|
+
proxyReadyAt: handoffAt,
|
|
6235
|
+
startedAt: handoffAt,
|
|
6236
|
+
reloadRequestedAt: void 0,
|
|
6237
|
+
error: void 0,
|
|
6238
|
+
updatedAt: handoffAt
|
|
6239
|
+
}));
|
|
6240
|
+
if (readyResult.message) {
|
|
6241
|
+
writePmLog(stdoutLog, readyResult.message);
|
|
6242
|
+
}
|
|
6243
|
+
writePmLog(stdoutLog, `${usesPmInheritedListener(latestRecord) ? "shared listener" : "proxy handoff"} activated on ${latestRecord.proxy.host ?? "0.0.0.0"}:${latestRecord.proxy.port}`);
|
|
6244
|
+
child = replacementChild;
|
|
6245
|
+
childIpcState = replacementIpcState;
|
|
6246
|
+
childStartedAt = Date.now();
|
|
6247
|
+
activeChild = replacementChild;
|
|
6248
|
+
childWaitState = { settled: false, result: void 0 };
|
|
6249
|
+
void waitForManagedChildExit(replacementChild).then((result) => {
|
|
6250
|
+
childWaitState.result = result;
|
|
6251
|
+
childWaitState.settled = true;
|
|
6252
|
+
});
|
|
6253
|
+
controllers = await createPmChildControllers(
|
|
6254
|
+
replacementMonitorRecord,
|
|
6255
|
+
replacementChild,
|
|
6256
|
+
stdoutLog,
|
|
6257
|
+
stderrLog,
|
|
6258
|
+
requestPlannedRestart,
|
|
6259
|
+
() => {
|
|
6260
|
+
},
|
|
6261
|
+
{ ready: true, ipcController: replacementIpcState }
|
|
6262
|
+
);
|
|
6263
|
+
await delay(250);
|
|
6264
|
+
await previousControllers.stop();
|
|
6265
|
+
previousIpcState?.stop();
|
|
6266
|
+
await stopProxyManagedChild(previousChild, latestRecord, stderrLog);
|
|
6267
|
+
clearActiveChildStopTimer();
|
|
6268
|
+
if (isPmProxyOwner(latestRecord)) {
|
|
6269
|
+
syncOwnedProxyTargets(latestRecord.baseName);
|
|
6270
|
+
}
|
|
6271
|
+
continue;
|
|
6272
|
+
}
|
|
6273
|
+
await delay(25);
|
|
6274
|
+
}
|
|
6275
|
+
const exitResult = childWaitState.result;
|
|
6276
|
+
await controllers.stop();
|
|
6277
|
+
childIpcState?.stop();
|
|
4980
6278
|
clearActiveChildStopTimer();
|
|
4981
6279
|
activeChild = null;
|
|
4982
6280
|
const exitCode = waitForExit(exitResult.code, exitResult.signal);
|
|
@@ -4992,56 +6290,77 @@ error: ${text}`);
|
|
|
4992
6290
|
if (stopRequested || current.desiredState === "stopped") {
|
|
4993
6291
|
break;
|
|
4994
6292
|
}
|
|
4995
|
-
const shouldRestartForExit = plannedRestart ? true : current.restartPolicy === "always" ? true : current.restartPolicy === "on-failure" ? exitCode !== 0 || Boolean(exitResult.error) : false;
|
|
6293
|
+
const shouldRestartForExit = plannedRestart?.kind === "memory-stop" ? false : plannedRestart ? true : current.restartPolicy === "always" ? true : current.restartPolicy === "on-failure" ? exitCode !== 0 || Boolean(exitResult.error) : false;
|
|
4996
6294
|
if (!shouldRestartForExit) {
|
|
4997
6295
|
persist((latestRecord) => ({
|
|
4998
6296
|
...latestRecord,
|
|
4999
|
-
status: exitCode === 0 && !exitResult.error ? "exited" : "errored",
|
|
6297
|
+
status: plannedRestart?.kind === "memory-stop" ? "errored" : exitCode === 0 && !exitResult.error ? "exited" : "errored",
|
|
5000
6298
|
childPid: void 0,
|
|
6299
|
+
proxyTargetPort: void 0,
|
|
6300
|
+
proxyReadyAt: void 0,
|
|
5001
6301
|
runnerPid: void 0,
|
|
5002
6302
|
lastExitCode: exitCode,
|
|
5003
|
-
|
|
6303
|
+
reloadRequestedAt: void 0,
|
|
6304
|
+
error: plannedRestart?.kind === "memory-stop" ? plannedRestart.detail : exitCode === 0 && !exitResult.error ? void 0 : exitResult.error ?? `Process exited with code ${exitCode}.`,
|
|
5004
6305
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5005
6306
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5006
6307
|
}));
|
|
6308
|
+
if (isPmProxyOwner(current)) {
|
|
6309
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6310
|
+
}
|
|
5007
6311
|
return;
|
|
5008
6312
|
}
|
|
5009
6313
|
const shouldCountRestart = plannedRestart?.kind !== "watch";
|
|
5010
|
-
const baseRestartCount =
|
|
6314
|
+
const baseRestartCount = resolvePmRestartCountBase(current, wasStable, plannedRestart?.kind);
|
|
5011
6315
|
const nextRestartCount = shouldCountRestart ? baseRestartCount + 1 : current.restartCount ?? 0;
|
|
5012
6316
|
if (nextRestartCount > current.maxRestarts) {
|
|
5013
6317
|
persist((latestRecord) => ({
|
|
5014
6318
|
...latestRecord,
|
|
5015
6319
|
status: "errored",
|
|
5016
6320
|
childPid: void 0,
|
|
6321
|
+
proxyTargetPort: void 0,
|
|
6322
|
+
proxyReadyAt: void 0,
|
|
5017
6323
|
runnerPid: void 0,
|
|
5018
6324
|
restartCount: nextRestartCount,
|
|
6325
|
+
lastRestartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5019
6326
|
lastExitCode: exitCode,
|
|
6327
|
+
reloadRequestedAt: void 0,
|
|
5020
6328
|
error: plannedRestart ? `Reached max restart attempts (${current.maxRestarts}) after ${plannedRestart.kind} restart requests.` : `Reached max restart attempts (${current.maxRestarts}).`,
|
|
5021
6329
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5022
6330
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5023
6331
|
}));
|
|
5024
6332
|
writePmLog(stderrLog, `max restart attempts reached (${current.maxRestarts})`);
|
|
6333
|
+
if (isPmProxyOwner(current)) {
|
|
6334
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6335
|
+
}
|
|
5025
6336
|
return;
|
|
5026
6337
|
}
|
|
5027
6338
|
persist((latestRecord) => ({
|
|
5028
6339
|
...latestRecord,
|
|
5029
6340
|
status: "restarting",
|
|
5030
6341
|
childPid: void 0,
|
|
6342
|
+
proxyTargetPort: void 0,
|
|
6343
|
+
proxyReadyAt: void 0,
|
|
5031
6344
|
lastExitCode: exitCode,
|
|
5032
6345
|
restartCount: nextRestartCount,
|
|
6346
|
+
lastRestartAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6347
|
+
reloadRequestedAt: void 0,
|
|
5033
6348
|
error: void 0,
|
|
5034
6349
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5035
6350
|
}));
|
|
6351
|
+
if (isPmProxyOwner(current)) {
|
|
6352
|
+
syncOwnedProxyTargets(current.baseName);
|
|
6353
|
+
}
|
|
6354
|
+
const resolvedRestartDelay = resolvePmRestartDelay(current, nextRestartCount, shouldCountRestart && !wasStable && plannedRestart?.kind !== "watch");
|
|
5036
6355
|
if (plannedRestart) {
|
|
5037
6356
|
writePmLog(
|
|
5038
|
-
plannedRestart.kind === "health" ? stderrLog : stdoutLog,
|
|
5039
|
-
`restarting in ${
|
|
6357
|
+
plannedRestart.kind === "health" || plannedRestart.kind === "memory" || plannedRestart.kind === "memory-stop" || plannedRestart.kind === "startup" ? stderrLog : stdoutLog,
|
|
6358
|
+
`restarting in ${resolvedRestartDelay}ms after ${plannedRestart.kind}: ${plannedRestart.detail}`
|
|
5040
6359
|
);
|
|
5041
6360
|
} else {
|
|
5042
|
-
writePmLog(stdoutLog, `restarting in ${
|
|
6361
|
+
writePmLog(stdoutLog, `restarting in ${resolvedRestartDelay}ms`);
|
|
5043
6362
|
}
|
|
5044
|
-
await delay(
|
|
6363
|
+
await delay(resolvedRestartDelay);
|
|
5045
6364
|
}
|
|
5046
6365
|
} finally {
|
|
5047
6366
|
stopRequested = true;
|
|
@@ -5054,11 +6373,26 @@ error: ${text}`);
|
|
|
5054
6373
|
status: finalRecord.status === "errored" ? "errored" : finalRecord.status === "exited" ? "exited" : "stopped",
|
|
5055
6374
|
runnerPid: void 0,
|
|
5056
6375
|
childPid: void 0,
|
|
6376
|
+
proxyTargetPort: void 0,
|
|
6377
|
+
proxyReadyAt: void 0,
|
|
6378
|
+
reloadRequestedAt: void 0,
|
|
5057
6379
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5058
6380
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5059
6381
|
});
|
|
5060
6382
|
process.off("SIGINT", handleStopSignal);
|
|
5061
6383
|
process.off("SIGTERM", handleStopSignal);
|
|
6384
|
+
if (proxyTargetSyncTimer) {
|
|
6385
|
+
clearInterval(proxyTargetSyncTimer);
|
|
6386
|
+
proxyTargetSyncTimer = null;
|
|
6387
|
+
}
|
|
6388
|
+
if (proxyController) {
|
|
6389
|
+
await proxyController.close().catch(() => void 0);
|
|
6390
|
+
proxyController = null;
|
|
6391
|
+
}
|
|
6392
|
+
if (inheritedListener) {
|
|
6393
|
+
await new Promise((resolvePromise) => inheritedListener?.close(() => resolvePromise()));
|
|
6394
|
+
inheritedListener = null;
|
|
6395
|
+
}
|
|
5062
6396
|
await new Promise((resolvePromise) => stdoutLog.end(resolvePromise));
|
|
5063
6397
|
await new Promise((resolvePromise) => stderrLog.end(resolvePromise));
|
|
5064
6398
|
}
|
|
@@ -5112,23 +6446,26 @@ error: ${text}`);
|
|
|
5112
6446
|
stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5113
6447
|
};
|
|
5114
6448
|
writePmRecord(match.filePath, updated);
|
|
5115
|
-
const
|
|
6449
|
+
const stopTimeout = resolvePmStopTimeout(match.record);
|
|
6450
|
+
const runnerStopped = await waitForProcessTermination(match.record.runnerPid, stopTimeout);
|
|
5116
6451
|
const childStopped = await waitForProcessTermination(
|
|
5117
6452
|
match.record.childPid,
|
|
5118
|
-
runnerStopped ? DEFAULT_PM_STOP_POLL_MS :
|
|
6453
|
+
runnerStopped ? DEFAULT_PM_STOP_POLL_MS : stopTimeout
|
|
5119
6454
|
);
|
|
5120
6455
|
if (!runnerStopped && match.record.runnerPid && isProcessAlive(match.record.runnerPid)) {
|
|
5121
|
-
terminateProcessTree(match.record.runnerPid);
|
|
6456
|
+
terminateProcessTree(match.record.runnerPid, { force: true });
|
|
5122
6457
|
await waitForProcessTermination(match.record.runnerPid, DEFAULT_PM_STOP_POLL_MS);
|
|
5123
6458
|
}
|
|
5124
6459
|
if (!childStopped && match.record.childPid && isProcessAlive(match.record.childPid)) {
|
|
5125
|
-
terminateProcessTree(match.record.childPid);
|
|
6460
|
+
terminateProcessTree(match.record.childPid, { force: true });
|
|
5126
6461
|
await waitForProcessTermination(match.record.childPid, DEFAULT_PM_STOP_POLL_MS);
|
|
5127
6462
|
}
|
|
5128
6463
|
writePmRecord(match.filePath, {
|
|
5129
6464
|
...updated,
|
|
5130
6465
|
runnerPid: void 0,
|
|
5131
6466
|
childPid: void 0,
|
|
6467
|
+
proxyTargetPort: void 0,
|
|
6468
|
+
reloadRequestedAt: void 0,
|
|
5132
6469
|
status: "stopped",
|
|
5133
6470
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5134
6471
|
});
|
|
@@ -5197,13 +6534,13 @@ error: ${text}`);
|
|
|
5197
6534
|
|
|
5198
6535
|
// src/shares/workspace-package/roots.ts
|
|
5199
6536
|
function findPackageDirectory(startDir, resolveMatch) {
|
|
5200
|
-
let currentDir =
|
|
6537
|
+
let currentDir = resolve5(startDir);
|
|
5201
6538
|
while (true) {
|
|
5202
6539
|
const match = resolveMatch(currentDir);
|
|
5203
6540
|
if (match) {
|
|
5204
6541
|
return match;
|
|
5205
6542
|
}
|
|
5206
|
-
const parentDir =
|
|
6543
|
+
const parentDir = dirname3(currentDir);
|
|
5207
6544
|
if (parentDir === currentDir) {
|
|
5208
6545
|
return void 0;
|
|
5209
6546
|
}
|
|
@@ -5231,17 +6568,17 @@ error: ${text}`);
|
|
|
5231
6568
|
function getWorkspacePackageImportCandidates(packageRoot, specifier, options = {}) {
|
|
5232
6569
|
const subpath = specifier === "elit" ? "index" : specifier.slice("elit/".length);
|
|
5233
6570
|
const builtCandidates = options.preferredBuiltFormat === "cjs" ? [
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
6571
|
+
resolve5(packageRoot, "dist", `${subpath}.cjs`),
|
|
6572
|
+
resolve5(packageRoot, "dist", `${subpath}.js`),
|
|
6573
|
+
resolve5(packageRoot, "dist", `${subpath}.mjs`)
|
|
5237
6574
|
] : [
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
6575
|
+
resolve5(packageRoot, "dist", `${subpath}.mjs`),
|
|
6576
|
+
resolve5(packageRoot, "dist", `${subpath}.js`),
|
|
6577
|
+
resolve5(packageRoot, "dist", `${subpath}.cjs`)
|
|
5241
6578
|
];
|
|
5242
6579
|
const sourceCandidates = [
|
|
5243
|
-
|
|
5244
|
-
|
|
6580
|
+
resolve5(packageRoot, "src", `${subpath}.ts`),
|
|
6581
|
+
resolve5(packageRoot, "src", `${subpath}.tsx`)
|
|
5245
6582
|
];
|
|
5246
6583
|
return options.preferBuilt ? [...builtCandidates, ...sourceCandidates] : [...sourceCandidates, ...builtCandidates];
|
|
5247
6584
|
}
|
|
@@ -5276,7 +6613,7 @@ error: ${text}`);
|
|
|
5276
6613
|
// src/shares/config/loader.ts
|
|
5277
6614
|
function resolveConfigPath(cwd = process.cwd()) {
|
|
5278
6615
|
for (const configFile of ELIT_CONFIG_FILES) {
|
|
5279
|
-
const configPath =
|
|
6616
|
+
const configPath = resolve5(cwd, configFile);
|
|
5280
6617
|
if (existsSync4(configPath)) {
|
|
5281
6618
|
return configPath;
|
|
5282
6619
|
}
|
|
@@ -5305,7 +6642,7 @@ error: ${text}`);
|
|
|
5305
6642
|
if (ext === "ts" || ext === "mts") {
|
|
5306
6643
|
try {
|
|
5307
6644
|
const { build } = await Promise.resolve().then(() => __toESM(require_main()));
|
|
5308
|
-
const configDir =
|
|
6645
|
+
const configDir = dirname3(configPath);
|
|
5309
6646
|
const tempFile = join5(configDir, `.elit-config-${Date.now()}.mjs`);
|
|
5310
6647
|
const externalAllPlugin = {
|
|
5311
6648
|
name: "external-all",
|
|
@@ -5379,6 +6716,43 @@ error: ${text}`);
|
|
|
5379
6716
|
}
|
|
5380
6717
|
|
|
5381
6718
|
// src/cli/pm/commands.ts
|
|
6719
|
+
var PM_SIGNAL_NAMES = /* @__PURE__ */ new Set([
|
|
6720
|
+
"SIGABRT",
|
|
6721
|
+
"SIGALRM",
|
|
6722
|
+
"SIGBREAK",
|
|
6723
|
+
"SIGBUS",
|
|
6724
|
+
"SIGCHLD",
|
|
6725
|
+
"SIGCONT",
|
|
6726
|
+
"SIGFPE",
|
|
6727
|
+
"SIGHUP",
|
|
6728
|
+
"SIGILL",
|
|
6729
|
+
"SIGINT",
|
|
6730
|
+
"SIGIO",
|
|
6731
|
+
"SIGIOT",
|
|
6732
|
+
"SIGKILL",
|
|
6733
|
+
"SIGPIPE",
|
|
6734
|
+
"SIGPOLL",
|
|
6735
|
+
"SIGPROF",
|
|
6736
|
+
"SIGPWR",
|
|
6737
|
+
"SIGQUIT",
|
|
6738
|
+
"SIGSEGV",
|
|
6739
|
+
"SIGSTKFLT",
|
|
6740
|
+
"SIGSTOP",
|
|
6741
|
+
"SIGSYS",
|
|
6742
|
+
"SIGTERM",
|
|
6743
|
+
"SIGTRAP",
|
|
6744
|
+
"SIGTSTP",
|
|
6745
|
+
"SIGTTIN",
|
|
6746
|
+
"SIGTTOU",
|
|
6747
|
+
"SIGUNUSED",
|
|
6748
|
+
"SIGURG",
|
|
6749
|
+
"SIGUSR1",
|
|
6750
|
+
"SIGUSR2",
|
|
6751
|
+
"SIGVTALRM",
|
|
6752
|
+
"SIGWINCH",
|
|
6753
|
+
"SIGXCPU",
|
|
6754
|
+
"SIGXFSZ"
|
|
6755
|
+
]);
|
|
5382
6756
|
async function runPmStart(args) {
|
|
5383
6757
|
const parsed = parsePmStartArgs(args);
|
|
5384
6758
|
const workspaceRoot = process.cwd();
|
|
@@ -5411,9 +6785,130 @@ error: ${text}`);
|
|
|
5411
6785
|
if (value === "all") {
|
|
5412
6786
|
return listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
5413
6787
|
}
|
|
6788
|
+
const groupMatches = findPmGroupMatches(paths, value);
|
|
6789
|
+
if (groupMatches.length > 0) {
|
|
6790
|
+
return groupMatches.map(syncPmRecordLiveness);
|
|
6791
|
+
}
|
|
5414
6792
|
const match = findPmRecordMatch(paths, value);
|
|
5415
6793
|
return match ? [syncPmRecordLiveness(match)] : [];
|
|
5416
6794
|
}
|
|
6795
|
+
function sortPmMatchesByInstance(matches) {
|
|
6796
|
+
return [...matches].sort((left, right) => left.record.instanceIndex - right.record.instanceIndex);
|
|
6797
|
+
}
|
|
6798
|
+
function resolveInspectableMatch(paths, value) {
|
|
6799
|
+
const exactMatch = findPmRecordMatch(paths, value);
|
|
6800
|
+
const groupMatches = findPmGroupMatches(paths, value).map(syncPmRecordLiveness);
|
|
6801
|
+
if (groupMatches.length > 1 && exactMatch?.record.baseName === value && exactMatch.record.name === value) {
|
|
6802
|
+
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}.`);
|
|
6803
|
+
}
|
|
6804
|
+
if (exactMatch) {
|
|
6805
|
+
return syncPmRecordLiveness(exactMatch);
|
|
6806
|
+
}
|
|
6807
|
+
return groupMatches[0];
|
|
6808
|
+
}
|
|
6809
|
+
function rebuildPmRecordDefinition(record, targetInstances = record.instances) {
|
|
6810
|
+
const definition = resolvePmAppDefinition(
|
|
6811
|
+
toPmAppConfig(record),
|
|
6812
|
+
{ name: record.baseName, env: {}, watchPaths: [], watchIgnore: [], instances: targetInstances },
|
|
6813
|
+
process.cwd(),
|
|
6814
|
+
record.source
|
|
6815
|
+
);
|
|
6816
|
+
return {
|
|
6817
|
+
...definition,
|
|
6818
|
+
name: record.name,
|
|
6819
|
+
baseName: record.baseName,
|
|
6820
|
+
instanceIndex: record.instanceIndex,
|
|
6821
|
+
instances: targetInstances
|
|
6822
|
+
};
|
|
6823
|
+
}
|
|
6824
|
+
function rebuildPmSavedDefinition(app) {
|
|
6825
|
+
const definition = resolvePmAppDefinition(
|
|
6826
|
+
toSavedPmAppConfig(app),
|
|
6827
|
+
{ name: app.baseName, env: {}, watchPaths: [], watchIgnore: [], instances: app.instances },
|
|
6828
|
+
process.cwd(),
|
|
6829
|
+
"cli"
|
|
6830
|
+
);
|
|
6831
|
+
return {
|
|
6832
|
+
...definition,
|
|
6833
|
+
name: app.name,
|
|
6834
|
+
baseName: app.baseName,
|
|
6835
|
+
instanceIndex: app.instanceIndex,
|
|
6836
|
+
instances: app.instances
|
|
6837
|
+
};
|
|
6838
|
+
}
|
|
6839
|
+
function deletePmMatches(matches) {
|
|
6840
|
+
for (const match of matches) {
|
|
6841
|
+
if ((0, import_node_fs5.existsSync)(match.record.logFiles.out)) {
|
|
6842
|
+
(0, import_node_fs5.rmSync)(match.record.logFiles.out, { force: true });
|
|
6843
|
+
}
|
|
6844
|
+
if ((0, import_node_fs5.existsSync)(match.record.logFiles.err)) {
|
|
6845
|
+
(0, import_node_fs5.rmSync)(match.record.logFiles.err, { force: true });
|
|
6846
|
+
}
|
|
6847
|
+
(0, import_node_fs5.rmSync)(match.filePath, { force: true });
|
|
6848
|
+
}
|
|
6849
|
+
}
|
|
6850
|
+
function updatePmInstanceCount(matches, instances) {
|
|
6851
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6852
|
+
for (const match of matches) {
|
|
6853
|
+
writePmRecord(match.filePath, {
|
|
6854
|
+
...match.record,
|
|
6855
|
+
instances,
|
|
6856
|
+
updatedAt: now
|
|
6857
|
+
});
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
function normalizePmSignalName(value) {
|
|
6861
|
+
const trimmed = value.trim().toUpperCase();
|
|
6862
|
+
const signalName = trimmed.startsWith("SIG") ? trimmed : `SIG${trimmed}`;
|
|
6863
|
+
if (!PM_SIGNAL_NAMES.has(signalName)) {
|
|
6864
|
+
throw new Error(`Unsupported pm signal: ${value}`);
|
|
6865
|
+
}
|
|
6866
|
+
return signalName;
|
|
6867
|
+
}
|
|
6868
|
+
function resolveSignalablePid(record) {
|
|
6869
|
+
if (record.childPid && record.childPid > 0) {
|
|
6870
|
+
return record.childPid;
|
|
6871
|
+
}
|
|
6872
|
+
if (record.runnerPid && record.runnerPid > 0) {
|
|
6873
|
+
return record.runnerPid;
|
|
6874
|
+
}
|
|
6875
|
+
return void 0;
|
|
6876
|
+
}
|
|
6877
|
+
function groupPmMatchesByBaseName(matches) {
|
|
6878
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
6879
|
+
for (const match of matches) {
|
|
6880
|
+
const group = grouped.get(match.record.baseName);
|
|
6881
|
+
if (group) {
|
|
6882
|
+
group.push(match);
|
|
6883
|
+
continue;
|
|
6884
|
+
}
|
|
6885
|
+
grouped.set(match.record.baseName, [match]);
|
|
6886
|
+
}
|
|
6887
|
+
return [...grouped.entries()].sort((left, right) => left[0].localeCompare(right[0])).map(([, group]) => sortPmMatchesByInstance(group));
|
|
6888
|
+
}
|
|
6889
|
+
async function waitForPmRecordOnline(filePath, timeoutMs) {
|
|
6890
|
+
const deadline = Date.now() + timeoutMs;
|
|
6891
|
+
while (Date.now() < deadline) {
|
|
6892
|
+
if (!(0, import_node_fs5.existsSync)(filePath)) {
|
|
6893
|
+
break;
|
|
6894
|
+
}
|
|
6895
|
+
const record = readPmRecord(filePath);
|
|
6896
|
+
if (record.status === "online") {
|
|
6897
|
+
return record;
|
|
6898
|
+
}
|
|
6899
|
+
if (record.status === "errored" || record.status === "exited" || record.status === "stopped") {
|
|
6900
|
+
throw new Error(record.error ?? `Process ${record.name} failed while reloading.`);
|
|
6901
|
+
}
|
|
6902
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));
|
|
6903
|
+
}
|
|
6904
|
+
throw new Error(`Timed out waiting for the reloaded process to become online after ${timeoutMs}ms.`);
|
|
6905
|
+
}
|
|
6906
|
+
function resolvePmReloadReadyTimeout(record) {
|
|
6907
|
+
return record.waitReady || record.proxy?.strategy === "inherit" ? Math.max(record.listenTimeout + 1e3, 2e3) : Math.max(record.restartDelay + 1e3, 2e3);
|
|
6908
|
+
}
|
|
6909
|
+
function supportsPmProxyReload2(record) {
|
|
6910
|
+
return Boolean(record.proxy) && record.instances === 1 && Boolean(record.runnerPid);
|
|
6911
|
+
}
|
|
5417
6912
|
function padCell(value, width) {
|
|
5418
6913
|
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
5419
6914
|
}
|
|
@@ -5424,8 +6919,255 @@ error: ${text}`);
|
|
|
5424
6919
|
const lines = (0, import_node_fs5.readFileSync)(filePath, "utf8").split(/\r?\n/).filter((line) => line.length > 0);
|
|
5425
6920
|
return lines.slice(-lineCount).join(import_node_os2.EOL);
|
|
5426
6921
|
}
|
|
5427
|
-
function
|
|
5428
|
-
|
|
6922
|
+
function listPmMatches(paths) {
|
|
6923
|
+
return listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
6924
|
+
}
|
|
6925
|
+
function isPmRecordActive(record) {
|
|
6926
|
+
return record.desiredState === "running" && (record.status === "starting" || record.status === "online" || record.status === "restarting");
|
|
6927
|
+
}
|
|
6928
|
+
function resolvePmUptimeMs(record) {
|
|
6929
|
+
if (!isPmRecordActive(record) || !record.startedAt) {
|
|
6930
|
+
return void 0;
|
|
6931
|
+
}
|
|
6932
|
+
const startedTime = Date.parse(record.startedAt);
|
|
6933
|
+
if (Number.isNaN(startedTime)) {
|
|
6934
|
+
return void 0;
|
|
6935
|
+
}
|
|
6936
|
+
return Math.max(0, Date.now() - startedTime);
|
|
6937
|
+
}
|
|
6938
|
+
function resolvePmLiveMetrics(record) {
|
|
6939
|
+
const uptimeMs = resolvePmUptimeMs(record);
|
|
6940
|
+
if (!isPmRecordActive(record) || !record.childPid) {
|
|
6941
|
+
return { uptimeMs };
|
|
6942
|
+
}
|
|
6943
|
+
const sampledMetrics = samplePmProcessMetrics(record.childPid);
|
|
6944
|
+
return {
|
|
6945
|
+
...sampledMetrics,
|
|
6946
|
+
uptimeMs,
|
|
6947
|
+
updatedAt: sampledMetrics.cpuPercent !== void 0 || sampledMetrics.memoryRssBytes !== void 0 ? (/* @__PURE__ */ new Date()).toISOString() : void 0
|
|
6948
|
+
};
|
|
6949
|
+
}
|
|
6950
|
+
function toPmDisplayRecord(record) {
|
|
6951
|
+
return {
|
|
6952
|
+
record,
|
|
6953
|
+
liveMetrics: resolvePmLiveMetrics(record)
|
|
6954
|
+
};
|
|
6955
|
+
}
|
|
6956
|
+
function serializePmRecord(record) {
|
|
6957
|
+
return {
|
|
6958
|
+
...record,
|
|
6959
|
+
liveMetrics: resolvePmLiveMetrics(record)
|
|
6960
|
+
};
|
|
6961
|
+
}
|
|
6962
|
+
function parsePmFormatOption(args, index, option) {
|
|
6963
|
+
let value;
|
|
6964
|
+
if (option.startsWith("--format=")) {
|
|
6965
|
+
value = option.slice("--format=".length);
|
|
6966
|
+
} else {
|
|
6967
|
+
value = readRequiredValue(args, index + 1, "--format");
|
|
6968
|
+
index += 1;
|
|
6969
|
+
}
|
|
6970
|
+
if (value !== "table" && value !== "json") {
|
|
6971
|
+
throw new Error(`Unsupported pm output format: ${value}`);
|
|
6972
|
+
}
|
|
6973
|
+
return {
|
|
6974
|
+
format: value,
|
|
6975
|
+
nextIndex: index
|
|
6976
|
+
};
|
|
6977
|
+
}
|
|
6978
|
+
function parsePmListArgs(args) {
|
|
6979
|
+
let format = "table";
|
|
6980
|
+
for (let index = 0; index < args.length; index++) {
|
|
6981
|
+
const arg = args[index];
|
|
6982
|
+
switch (arg) {
|
|
6983
|
+
case "--json":
|
|
6984
|
+
format = "json";
|
|
6985
|
+
break;
|
|
6986
|
+
case "--format":
|
|
6987
|
+
default:
|
|
6988
|
+
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
6989
|
+
const parsed = parsePmFormatOption(args, index, arg);
|
|
6990
|
+
format = parsed.format;
|
|
6991
|
+
index = parsed.nextIndex;
|
|
6992
|
+
break;
|
|
6993
|
+
}
|
|
6994
|
+
throw new Error(`Unknown pm list option: ${arg}`);
|
|
6995
|
+
}
|
|
6996
|
+
}
|
|
6997
|
+
return { format };
|
|
6998
|
+
}
|
|
6999
|
+
function parsePmShowArgs(args) {
|
|
7000
|
+
let format = "text";
|
|
7001
|
+
let name;
|
|
7002
|
+
for (let index = 0; index < args.length; index++) {
|
|
7003
|
+
const arg = args[index];
|
|
7004
|
+
switch (arg) {
|
|
7005
|
+
case "--json":
|
|
7006
|
+
format = "json";
|
|
7007
|
+
break;
|
|
7008
|
+
case "--format":
|
|
7009
|
+
default:
|
|
7010
|
+
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
7011
|
+
const parsed = parsePmFormatOption(args, index, arg);
|
|
7012
|
+
format = parsed.format === "json" ? "json" : "text";
|
|
7013
|
+
index = parsed.nextIndex;
|
|
7014
|
+
break;
|
|
7015
|
+
}
|
|
7016
|
+
if (arg.startsWith("-")) {
|
|
7017
|
+
throw new Error(`Unknown pm show option: ${arg}`);
|
|
7018
|
+
}
|
|
7019
|
+
if (name) {
|
|
7020
|
+
throw new Error("pm show accepts exactly one process name.");
|
|
7021
|
+
}
|
|
7022
|
+
name = arg;
|
|
7023
|
+
break;
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
7026
|
+
if (!name) {
|
|
7027
|
+
throw new Error("Usage: elit pm show <name> [--json]");
|
|
7028
|
+
}
|
|
7029
|
+
return { name, format };
|
|
7030
|
+
}
|
|
7031
|
+
function formatPmDuration(durationMs) {
|
|
7032
|
+
if (durationMs < 1e3) {
|
|
7033
|
+
return `${durationMs}ms`;
|
|
7034
|
+
}
|
|
7035
|
+
const totalSeconds = Math.floor(durationMs / 1e3);
|
|
7036
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
7037
|
+
const hours = Math.floor(totalSeconds % 86400 / 3600);
|
|
7038
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
7039
|
+
const seconds = totalSeconds % 60;
|
|
7040
|
+
const parts = [];
|
|
7041
|
+
if (days > 0) {
|
|
7042
|
+
parts.push(`${days}d`);
|
|
7043
|
+
}
|
|
7044
|
+
if (hours > 0) {
|
|
7045
|
+
parts.push(`${hours}h`);
|
|
7046
|
+
}
|
|
7047
|
+
if (minutes > 0) {
|
|
7048
|
+
parts.push(`${minutes}m`);
|
|
7049
|
+
}
|
|
7050
|
+
if (seconds > 0 || parts.length === 0) {
|
|
7051
|
+
parts.push(`${seconds}s`);
|
|
7052
|
+
}
|
|
7053
|
+
return parts.slice(0, 2).join(" ");
|
|
7054
|
+
}
|
|
7055
|
+
function formatPmCpuPercent(cpuPercent) {
|
|
7056
|
+
if (cpuPercent === void 0 || !Number.isFinite(cpuPercent)) {
|
|
7057
|
+
return "-";
|
|
7058
|
+
}
|
|
7059
|
+
return `${cpuPercent >= 100 ? cpuPercent.toFixed(0) : cpuPercent.toFixed(1)}%`;
|
|
7060
|
+
}
|
|
7061
|
+
function formatPmMemory(memoryRssBytes) {
|
|
7062
|
+
if (memoryRssBytes === void 0 || !Number.isFinite(memoryRssBytes) || memoryRssBytes < 0) {
|
|
7063
|
+
return "-";
|
|
7064
|
+
}
|
|
7065
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
7066
|
+
let value = memoryRssBytes;
|
|
7067
|
+
let unitIndex = 0;
|
|
7068
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
7069
|
+
value /= 1024;
|
|
7070
|
+
unitIndex += 1;
|
|
7071
|
+
}
|
|
7072
|
+
const formatted = value >= 10 || unitIndex === 0 ? value.toFixed(0) : value.toFixed(1);
|
|
7073
|
+
return `${formatted}${units[unitIndex]}`;
|
|
7074
|
+
}
|
|
7075
|
+
function formatPmUptime(uptimeMs) {
|
|
7076
|
+
if (uptimeMs === void 0 || !Number.isFinite(uptimeMs)) {
|
|
7077
|
+
return "-";
|
|
7078
|
+
}
|
|
7079
|
+
return formatPmDuration(Math.max(0, uptimeMs));
|
|
7080
|
+
}
|
|
7081
|
+
function formatPmTarget(record) {
|
|
7082
|
+
if (record.script) {
|
|
7083
|
+
return record.script;
|
|
7084
|
+
}
|
|
7085
|
+
if (record.file) {
|
|
7086
|
+
return record.file;
|
|
7087
|
+
}
|
|
7088
|
+
if (record.wapk) {
|
|
7089
|
+
return record.wapk;
|
|
7090
|
+
}
|
|
7091
|
+
return "-";
|
|
7092
|
+
}
|
|
7093
|
+
function pushPmDetail(lines, label, value) {
|
|
7094
|
+
lines.push(`${padCell(`${label}:`, 18)} ${value}`);
|
|
7095
|
+
}
|
|
7096
|
+
function pushPmDetailList(lines, label, values) {
|
|
7097
|
+
if (values.length === 0) {
|
|
7098
|
+
pushPmDetail(lines, label, "-");
|
|
7099
|
+
return;
|
|
7100
|
+
}
|
|
7101
|
+
pushPmDetail(lines, label, values[0] ?? "-");
|
|
7102
|
+
for (const value of values.slice(1)) {
|
|
7103
|
+
lines.push(`${" ".repeat(20)} ${value}`);
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
function formatPmRecordDetails(record, liveMetrics) {
|
|
7107
|
+
const lines = [`Process: ${record.name}`];
|
|
7108
|
+
pushPmDetail(lines, "id", record.id);
|
|
7109
|
+
pushPmDetail(lines, "status", record.status);
|
|
7110
|
+
pushPmDetail(lines, "desired state", record.desiredState);
|
|
7111
|
+
pushPmDetail(lines, "instance", `${record.instanceIndex}/${record.instances}`);
|
|
7112
|
+
pushPmDetail(lines, "cpu", formatPmCpuPercent(liveMetrics.cpuPercent));
|
|
7113
|
+
pushPmDetail(lines, "memory", formatPmMemory(liveMetrics.memoryRssBytes));
|
|
7114
|
+
pushPmDetail(lines, "uptime", formatPmUptime(liveMetrics.uptimeMs));
|
|
7115
|
+
pushPmDetail(lines, "type", record.type);
|
|
7116
|
+
pushPmDetail(lines, "source", record.source);
|
|
7117
|
+
pushPmDetail(lines, "runtime", record.runtime ?? "-");
|
|
7118
|
+
pushPmDetail(lines, "cwd", record.cwd);
|
|
7119
|
+
pushPmDetail(lines, "target", formatPmTarget(record));
|
|
7120
|
+
pushPmDetail(lines, "command", record.commandPreview || "-");
|
|
7121
|
+
pushPmDetail(lines, "runner pid", record.runnerPid ? String(record.runnerPid) : "-");
|
|
7122
|
+
pushPmDetail(lines, "child pid", record.childPid ? String(record.childPid) : "-");
|
|
7123
|
+
pushPmDetail(lines, "restart count", `${record.restartCount}/${record.maxRestarts}`);
|
|
7124
|
+
pushPmDetail(lines, "restart policy", record.restartPolicy);
|
|
7125
|
+
pushPmDetail(lines, "proxy", record.proxy ? `http://${record.proxy.host ?? "0.0.0.0"}:${record.proxy.port}` : "-");
|
|
7126
|
+
pushPmDetail(lines, "proxy strategy", record.proxy?.strategy ?? "-");
|
|
7127
|
+
pushPmDetail(lines, "proxy target", record.proxy && record.proxyTargetPort ? `${record.proxy.targetHost ?? "127.0.0.1"}:${record.proxyTargetPort}` : "-");
|
|
7128
|
+
pushPmDetail(lines, "max memory", record.maxMemoryBytes ? formatPmMemory(record.maxMemoryBytes) : "-");
|
|
7129
|
+
pushPmDetail(lines, "memory action", record.memoryAction ?? "-");
|
|
7130
|
+
pushPmDetail(lines, "cron restart", record.cronRestart ?? "-");
|
|
7131
|
+
pushPmDetail(lines, "exp backoff", record.expBackoffRestartDelay ? formatPmDuration(record.expBackoffRestartDelay) : "-");
|
|
7132
|
+
pushPmDetail(lines, "exp backoff max", record.expBackoffRestartMaxDelay ? formatPmDuration(record.expBackoffRestartMaxDelay) : "-");
|
|
7133
|
+
pushPmDetail(lines, "restart window", record.restartWindow ? formatPmDuration(record.restartWindow) : "-");
|
|
7134
|
+
pushPmDetail(lines, "wait ready", record.waitReady ? "enabled" : "disabled");
|
|
7135
|
+
pushPmDetail(lines, "listen timeout", record.waitReady ? formatPmDuration(record.listenTimeout) : "-");
|
|
7136
|
+
pushPmDetail(lines, "restart delay", formatPmDuration(record.restartDelay));
|
|
7137
|
+
pushPmDetail(lines, "kill timeout", formatPmDuration(record.killTimeout));
|
|
7138
|
+
pushPmDetail(lines, "min uptime", formatPmDuration(record.minUptime));
|
|
7139
|
+
pushPmDetail(lines, "autorestart", record.autorestart ? "enabled" : "disabled");
|
|
7140
|
+
pushPmDetail(lines, "watch", record.watch ? "enabled" : "disabled");
|
|
7141
|
+
pushPmDetail(lines, "watch debounce", record.watch ? formatPmDuration(record.watchDebounce) : "-");
|
|
7142
|
+
pushPmDetailList(lines, "watch paths", record.watchPaths);
|
|
7143
|
+
pushPmDetailList(lines, "watch ignore", record.watchIgnore);
|
|
7144
|
+
if (record.healthCheck) {
|
|
7145
|
+
pushPmDetail(lines, "health check", record.healthCheck.url);
|
|
7146
|
+
pushPmDetail(lines, "health grace", formatPmDuration(record.healthCheck.gracePeriod));
|
|
7147
|
+
pushPmDetail(lines, "health interval", formatPmDuration(record.healthCheck.interval));
|
|
7148
|
+
pushPmDetail(lines, "health timeout", formatPmDuration(record.healthCheck.timeout));
|
|
7149
|
+
pushPmDetail(lines, "health failures", String(record.healthCheck.maxFailures));
|
|
7150
|
+
} else {
|
|
7151
|
+
pushPmDetail(lines, "health check", "-");
|
|
7152
|
+
}
|
|
7153
|
+
pushPmDetailList(lines, "env", Object.entries(record.env).map(([key, value]) => `${key}=${value}`));
|
|
7154
|
+
pushPmDetail(lines, "stdout log", record.logFiles.out);
|
|
7155
|
+
pushPmDetail(lines, "stderr log", record.logFiles.err);
|
|
7156
|
+
pushPmDetail(lines, "created at", record.createdAt);
|
|
7157
|
+
pushPmDetail(lines, "updated at", record.updatedAt);
|
|
7158
|
+
pushPmDetail(lines, "metrics at", liveMetrics.updatedAt ?? "-");
|
|
7159
|
+
pushPmDetail(lines, "started at", record.startedAt ?? "-");
|
|
7160
|
+
pushPmDetail(lines, "stopped at", record.stoppedAt ?? "-");
|
|
7161
|
+
pushPmDetail(lines, "last exit", record.lastExitCode === void 0 ? "-" : String(record.lastExitCode));
|
|
7162
|
+
pushPmDetail(lines, "error", record.error ?? "-");
|
|
7163
|
+
return lines.join(import_node_os2.EOL);
|
|
7164
|
+
}
|
|
7165
|
+
function printPmList(paths, format = "table") {
|
|
7166
|
+
const matches = listPmMatches(paths).map((match) => toPmDisplayRecord(match.record));
|
|
7167
|
+
if (format === "json") {
|
|
7168
|
+
console.log(JSON.stringify(matches.map((match) => ({ ...match.record, liveMetrics: match.liveMetrics })), null, 2));
|
|
7169
|
+
return;
|
|
7170
|
+
}
|
|
5429
7171
|
if (matches.length === 0) {
|
|
5430
7172
|
console.log("No managed processes found.");
|
|
5431
7173
|
return;
|
|
@@ -5434,22 +7176,47 @@ error: ${text}`);
|
|
|
5434
7176
|
padCell("name", 20),
|
|
5435
7177
|
padCell("status", 12),
|
|
5436
7178
|
padCell("pid", 8),
|
|
7179
|
+
padCell("cpu", 8),
|
|
7180
|
+
padCell("memory", 10),
|
|
7181
|
+
padCell("uptime", 10),
|
|
5437
7182
|
padCell("restarts", 10),
|
|
5438
7183
|
padCell("type", 8),
|
|
5439
7184
|
"runtime"
|
|
5440
7185
|
];
|
|
5441
7186
|
console.log(headers.join(" "));
|
|
5442
|
-
for (const { record } of matches) {
|
|
7187
|
+
for (const { record, liveMetrics } of matches) {
|
|
5443
7188
|
console.log([
|
|
5444
7189
|
padCell(record.name, 20),
|
|
5445
7190
|
padCell(record.status, 12),
|
|
5446
7191
|
padCell(record.childPid ? String(record.childPid) : "-", 8),
|
|
7192
|
+
padCell(formatPmCpuPercent(liveMetrics.cpuPercent), 8),
|
|
7193
|
+
padCell(formatPmMemory(liveMetrics.memoryRssBytes), 10),
|
|
7194
|
+
padCell(formatPmUptime(liveMetrics.uptimeMs), 10),
|
|
5447
7195
|
padCell(String(record.restartCount ?? 0), 10),
|
|
5448
7196
|
padCell(record.type, 8),
|
|
5449
7197
|
record.runtime ?? "-"
|
|
5450
7198
|
].join(" "));
|
|
5451
7199
|
}
|
|
5452
7200
|
}
|
|
7201
|
+
async function runPmList(args) {
|
|
7202
|
+
const options = parsePmListArgs(args);
|
|
7203
|
+
const { paths } = await loadPmContext();
|
|
7204
|
+
printPmList(paths, options.format);
|
|
7205
|
+
}
|
|
7206
|
+
async function runPmShow(args) {
|
|
7207
|
+
const options = parsePmShowArgs(args);
|
|
7208
|
+
const { paths } = await loadPmContext();
|
|
7209
|
+
const match = resolveInspectableMatch(paths, options.name);
|
|
7210
|
+
if (!match) {
|
|
7211
|
+
throw new Error(`No managed process found for: ${options.name}`);
|
|
7212
|
+
}
|
|
7213
|
+
const synced = syncPmRecordLiveness(match);
|
|
7214
|
+
if (options.format === "json") {
|
|
7215
|
+
console.log(JSON.stringify(serializePmRecord(synced.record), null, 2));
|
|
7216
|
+
return;
|
|
7217
|
+
}
|
|
7218
|
+
console.log(formatPmRecordDetails(synced.record, resolvePmLiveMetrics(synced.record)));
|
|
7219
|
+
}
|
|
5453
7220
|
async function runPmStop(args) {
|
|
5454
7221
|
const target = args[0];
|
|
5455
7222
|
if (!target) {
|
|
@@ -5476,17 +7243,62 @@ error: ${text}`);
|
|
|
5476
7243
|
await stopPmMatches(matches);
|
|
5477
7244
|
const restarted = [];
|
|
5478
7245
|
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
|
-
);
|
|
7246
|
+
const definition = rebuildPmRecordDefinition(match.record);
|
|
5485
7247
|
await startManagedProcess(definition, paths);
|
|
5486
7248
|
restarted.push(match.record.name);
|
|
5487
7249
|
}
|
|
5488
7250
|
console.log(`[pm] restarted ${restarted.join(", ")}`);
|
|
5489
7251
|
}
|
|
7252
|
+
async function runPmReload(args) {
|
|
7253
|
+
const target = args[0];
|
|
7254
|
+
if (!target) {
|
|
7255
|
+
throw new Error("Usage: elit pm reload <name|all>");
|
|
7256
|
+
}
|
|
7257
|
+
const { paths } = await loadPmContext();
|
|
7258
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7259
|
+
if (matches.length === 0) {
|
|
7260
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7261
|
+
}
|
|
7262
|
+
const reloaded = [];
|
|
7263
|
+
const errors = [];
|
|
7264
|
+
for (const group of groupPmMatchesByBaseName(matches)) {
|
|
7265
|
+
for (const match of group) {
|
|
7266
|
+
try {
|
|
7267
|
+
if (supportsPmProxyReload2(match.record)) {
|
|
7268
|
+
const reloadRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7269
|
+
writePmRecord(match.filePath, {
|
|
7270
|
+
...match.record,
|
|
7271
|
+
status: "restarting",
|
|
7272
|
+
reloadRequestedAt,
|
|
7273
|
+
updatedAt: reloadRequestedAt,
|
|
7274
|
+
error: void 0
|
|
7275
|
+
});
|
|
7276
|
+
await waitForPmRecordOnline(
|
|
7277
|
+
getPmRecordPath(paths, match.record.id),
|
|
7278
|
+
resolvePmReloadReadyTimeout(match.record)
|
|
7279
|
+
);
|
|
7280
|
+
reloaded.push(match.record.name);
|
|
7281
|
+
continue;
|
|
7282
|
+
}
|
|
7283
|
+
await stopPmMatches([match]);
|
|
7284
|
+
const definition = rebuildPmRecordDefinition(match.record);
|
|
7285
|
+
const startedRecord = await startManagedProcess(definition, paths);
|
|
7286
|
+
await waitForPmRecordOnline(
|
|
7287
|
+
getPmRecordPath(paths, startedRecord.id),
|
|
7288
|
+
resolvePmReloadReadyTimeout(startedRecord)
|
|
7289
|
+
);
|
|
7290
|
+
reloaded.push(match.record.name);
|
|
7291
|
+
} catch (error) {
|
|
7292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7293
|
+
errors.push(`[pm] ${match.record.name}: ${message}`);
|
|
7294
|
+
}
|
|
7295
|
+
}
|
|
7296
|
+
}
|
|
7297
|
+
if (errors.length > 0) {
|
|
7298
|
+
throw new Error([`[pm] reloaded ${reloaded.length} process${reloaded.length === 1 ? "" : "es"}`, ...errors].join(import_node_os2.EOL));
|
|
7299
|
+
}
|
|
7300
|
+
console.log(`[pm] reloaded ${reloaded.join(", ")}`);
|
|
7301
|
+
}
|
|
5490
7302
|
async function runPmSave() {
|
|
5491
7303
|
const { paths } = await loadPmContext();
|
|
5492
7304
|
ensurePmDirectories(paths);
|
|
@@ -5508,12 +7320,7 @@ error: ${text}`);
|
|
|
5508
7320
|
let restored = 0;
|
|
5509
7321
|
for (const app of dump.apps) {
|
|
5510
7322
|
try {
|
|
5511
|
-
const definition =
|
|
5512
|
-
toSavedPmAppConfig(app),
|
|
5513
|
-
{ name: app.name, env: {}, watchPaths: [], watchIgnore: [] },
|
|
5514
|
-
process.cwd(),
|
|
5515
|
-
"cli"
|
|
5516
|
-
);
|
|
7323
|
+
const definition = rebuildPmSavedDefinition(app);
|
|
5517
7324
|
await startManagedProcess(definition, paths);
|
|
5518
7325
|
restored += 1;
|
|
5519
7326
|
} catch (error) {
|
|
@@ -5537,16 +7344,127 @@ error: ${text}`);
|
|
|
5537
7344
|
throw new Error(`No managed process found for: ${target}`);
|
|
5538
7345
|
}
|
|
5539
7346
|
await stopPmMatches(matches);
|
|
7347
|
+
deletePmMatches(matches);
|
|
7348
|
+
console.log(`[pm] deleted ${matches.length} process${matches.length === 1 ? "" : "es"}`);
|
|
7349
|
+
}
|
|
7350
|
+
async function runPmScale(args) {
|
|
7351
|
+
const target = args[0];
|
|
7352
|
+
const countArg = args[1];
|
|
7353
|
+
if (!target || countArg === void 0 || args.length > 2) {
|
|
7354
|
+
throw new Error("Usage: elit pm scale <name> <count>");
|
|
7355
|
+
}
|
|
7356
|
+
const desiredCount = normalizeIntegerOption(countArg, "pm scale <count>", 0);
|
|
7357
|
+
const { config, paths } = await loadPmContext();
|
|
7358
|
+
const exactMatch = findPmRecordMatch(paths, target);
|
|
7359
|
+
const currentMatches = sortPmMatchesByInstance(resolveNamedMatches(paths, exactMatch?.record.baseName ?? target));
|
|
7360
|
+
if (currentMatches.length === 0) {
|
|
7361
|
+
if (desiredCount === 0) {
|
|
7362
|
+
console.log(`[pm] ${target} already scaled to 0 instances`);
|
|
7363
|
+
return;
|
|
7364
|
+
}
|
|
7365
|
+
const definitions = resolvePmStartDefinitions(
|
|
7366
|
+
{ name: target, env: {}, watchPaths: [], watchIgnore: [], instances: desiredCount },
|
|
7367
|
+
config,
|
|
7368
|
+
process.cwd()
|
|
7369
|
+
);
|
|
7370
|
+
for (const definition of definitions) {
|
|
7371
|
+
await startManagedProcess(definition, paths);
|
|
7372
|
+
}
|
|
7373
|
+
console.log(`[pm] scaled ${target} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7374
|
+
return;
|
|
7375
|
+
}
|
|
7376
|
+
const baseName = currentMatches[0]?.record.baseName ?? target;
|
|
7377
|
+
if (desiredCount === currentMatches.length) {
|
|
7378
|
+
console.log(`[pm] ${baseName} already scaled to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7379
|
+
return;
|
|
7380
|
+
}
|
|
7381
|
+
if (desiredCount === 0) {
|
|
7382
|
+
await stopPmMatches(currentMatches);
|
|
7383
|
+
deletePmMatches(currentMatches);
|
|
7384
|
+
console.log(`[pm] scaled ${baseName} to 0 instances`);
|
|
7385
|
+
return;
|
|
7386
|
+
}
|
|
7387
|
+
if (desiredCount < currentMatches.length) {
|
|
7388
|
+
const toRemove = [...currentMatches].sort((left, right) => right.record.instanceIndex - left.record.instanceIndex).slice(0, currentMatches.length - desiredCount);
|
|
7389
|
+
const remaining = currentMatches.filter((match) => !toRemove.some((removal) => removal.record.id === match.record.id));
|
|
7390
|
+
await stopPmMatches(toRemove);
|
|
7391
|
+
deletePmMatches(toRemove);
|
|
7392
|
+
updatePmInstanceCount(remaining, desiredCount);
|
|
7393
|
+
console.log(`[pm] scaled ${baseName} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7394
|
+
return;
|
|
7395
|
+
}
|
|
7396
|
+
updatePmInstanceCount(currentMatches, desiredCount);
|
|
7397
|
+
const baseRecord = currentMatches[0]?.record;
|
|
7398
|
+
if (!baseRecord) {
|
|
7399
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7400
|
+
}
|
|
7401
|
+
const baseDefinition = rebuildPmRecordDefinition(baseRecord, desiredCount);
|
|
7402
|
+
const expandedDefinitions = expandPmInstanceDefinitions(baseDefinition, desiredCount);
|
|
7403
|
+
const existingNames = new Set(currentMatches.map((match) => match.record.name));
|
|
7404
|
+
for (const definition of expandedDefinitions) {
|
|
7405
|
+
if (existingNames.has(definition.name)) {
|
|
7406
|
+
continue;
|
|
7407
|
+
}
|
|
7408
|
+
await startManagedProcess(definition, paths);
|
|
7409
|
+
}
|
|
7410
|
+
console.log(`[pm] scaled ${baseName} to ${desiredCount} instance${desiredCount === 1 ? "" : "s"}`);
|
|
7411
|
+
}
|
|
7412
|
+
async function runPmSendSignal(args) {
|
|
7413
|
+
const signalArg = args[0];
|
|
7414
|
+
const target = args[1];
|
|
7415
|
+
if (!signalArg || !target || args.length > 2) {
|
|
7416
|
+
throw new Error("Usage: elit pm send-signal <signal> <name|all>");
|
|
7417
|
+
}
|
|
7418
|
+
const signalName = normalizePmSignalName(signalArg);
|
|
7419
|
+
const { paths } = await loadPmContext();
|
|
7420
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7421
|
+
if (matches.length === 0) {
|
|
7422
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7423
|
+
}
|
|
7424
|
+
let signaled = 0;
|
|
7425
|
+
const errors = [];
|
|
5540
7426
|
for (const match of matches) {
|
|
5541
|
-
|
|
5542
|
-
|
|
7427
|
+
const pid = resolveSignalablePid(match.record);
|
|
7428
|
+
if (!pid) {
|
|
7429
|
+
continue;
|
|
5543
7430
|
}
|
|
5544
|
-
|
|
5545
|
-
(
|
|
7431
|
+
try {
|
|
7432
|
+
sendPmSignal(pid, signalName);
|
|
7433
|
+
signaled += 1;
|
|
7434
|
+
} catch (error) {
|
|
7435
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7436
|
+
errors.push(`[pm] ${match.record.name}: ${message}`);
|
|
5546
7437
|
}
|
|
5547
|
-
(0, import_node_fs5.rmSync)(match.filePath, { force: true });
|
|
5548
7438
|
}
|
|
5549
|
-
|
|
7439
|
+
if (errors.length > 0) {
|
|
7440
|
+
throw new Error([`[pm] sent ${signalName} to ${signaled} process${signaled === 1 ? "" : "es"}`, ...errors].join(import_node_os2.EOL));
|
|
7441
|
+
}
|
|
7442
|
+
if (signaled === 0) {
|
|
7443
|
+
throw new Error(`No running managed process found for: ${target}`);
|
|
7444
|
+
}
|
|
7445
|
+
console.log(`[pm] sent ${signalName} to ${signaled} process${signaled === 1 ? "" : "es"}`);
|
|
7446
|
+
}
|
|
7447
|
+
async function runPmReset(args) {
|
|
7448
|
+
const target = args[0];
|
|
7449
|
+
if (!target) {
|
|
7450
|
+
throw new Error("Usage: elit pm reset <name|all>");
|
|
7451
|
+
}
|
|
7452
|
+
const { paths } = await loadPmContext();
|
|
7453
|
+
const matches = resolveNamedMatches(paths, target);
|
|
7454
|
+
if (matches.length === 0) {
|
|
7455
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
7456
|
+
}
|
|
7457
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7458
|
+
for (const match of matches) {
|
|
7459
|
+
writePmRecord(match.filePath, {
|
|
7460
|
+
...match.record,
|
|
7461
|
+
restartCount: 0,
|
|
7462
|
+
lastExitCode: void 0,
|
|
7463
|
+
error: void 0,
|
|
7464
|
+
updatedAt: now
|
|
7465
|
+
});
|
|
7466
|
+
}
|
|
7467
|
+
console.log(`[pm] reset ${matches.length} process${matches.length === 1 ? "" : "es"}`);
|
|
5550
7468
|
}
|
|
5551
7469
|
async function runPmLogs(args) {
|
|
5552
7470
|
if (args.length === 0) {
|
|
@@ -5579,7 +7497,7 @@ error: ${text}`);
|
|
|
5579
7497
|
throw new Error("Usage: elit pm logs <name> [--lines <n>] [--stderr]");
|
|
5580
7498
|
}
|
|
5581
7499
|
const { paths } = await loadPmContext();
|
|
5582
|
-
const match =
|
|
7500
|
+
const match = resolveInspectableMatch(paths, name);
|
|
5583
7501
|
if (!match) {
|
|
5584
7502
|
throw new Error(`No managed process found for: ${name}`);
|
|
5585
7503
|
}
|
|
@@ -5603,17 +7521,36 @@ error: ${text}`);
|
|
|
5603
7521
|
await runPmStart(args.slice(1));
|
|
5604
7522
|
return;
|
|
5605
7523
|
case "list":
|
|
5606
|
-
case "ls":
|
|
5607
|
-
|
|
5608
|
-
|
|
7524
|
+
case "ls":
|
|
7525
|
+
await runPmList(args.slice(1));
|
|
7526
|
+
return;
|
|
7527
|
+
case "jlist":
|
|
7528
|
+
await runPmList(["--json", ...args.slice(1)]);
|
|
7529
|
+
return;
|
|
7530
|
+
case "show":
|
|
7531
|
+
case "describe":
|
|
7532
|
+
await runPmShow(args.slice(1));
|
|
5609
7533
|
return;
|
|
5610
|
-
}
|
|
5611
7534
|
case "stop":
|
|
5612
7535
|
await runPmStop(args.slice(1));
|
|
5613
7536
|
return;
|
|
5614
7537
|
case "restart":
|
|
5615
7538
|
await runPmRestart(args.slice(1));
|
|
5616
7539
|
return;
|
|
7540
|
+
case "reload":
|
|
7541
|
+
await runPmReload(args.slice(1));
|
|
7542
|
+
return;
|
|
7543
|
+
case "scale":
|
|
7544
|
+
await runPmScale(args.slice(1));
|
|
7545
|
+
return;
|
|
7546
|
+
case "send-signal":
|
|
7547
|
+
case "signal":
|
|
7548
|
+
case "sendSignal":
|
|
7549
|
+
await runPmSendSignal(args.slice(1));
|
|
7550
|
+
return;
|
|
7551
|
+
case "reset":
|
|
7552
|
+
await runPmReset(args.slice(1));
|
|
7553
|
+
return;
|
|
5617
7554
|
case "delete":
|
|
5618
7555
|
case "remove":
|
|
5619
7556
|
case "rm":
|