elit 3.6.6 → 3.6.7
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/dist/build.cjs +421 -331
- package/dist/build.d.ts +1 -19
- package/dist/build.js +420 -330
- package/dist/build.mjs +420 -330
- package/dist/chokidar.cjs +219 -182
- package/dist/chokidar.d.ts +25 -10
- package/dist/chokidar.js +217 -182
- package/dist/chokidar.mjs +218 -183
- package/dist/cli.cjs +22439 -21563
- package/dist/cli.d.ts +19 -37
- package/dist/cli.mjs +24113 -23252
- package/dist/config.cjs +357 -350
- package/dist/config.d.ts +17 -245
- package/dist/config.js +520 -515
- package/dist/config.mjs +346 -341
- package/dist/contracts-BeW9k0yZ.d.ts +54 -0
- package/dist/contracts-D7KIS-TK.d.ts +36 -0
- package/dist/coverage.cjs +448 -485
- package/dist/coverage.d.ts +13 -62
- package/dist/coverage.js +447 -484
- package/dist/coverage.mjs +447 -484
- package/dist/database.cjs +819 -828
- package/dist/database.d.ts +8 -24
- package/dist/database.js +818 -829
- package/dist/database.mjs +818 -829
- package/dist/desktop-auto-render.cjs +1700 -1523
- package/dist/desktop-auto-render.d.ts +4 -12
- package/dist/desktop-auto-render.js +1695 -1518
- package/dist/desktop-auto-render.mjs +1696 -1519
- package/dist/desktop.cjs +3 -1
- package/dist/desktop.d.ts +4 -1
- package/dist/desktop.js +1 -1
- package/dist/desktop.mjs +1 -1
- package/dist/dev-build.cjs +830 -0
- package/dist/dev-build.d.ts +53 -0
- package/dist/dev-build.js +3318 -0
- package/dist/dev-build.mjs +797 -0
- package/dist/dom.cjs +717 -591
- package/dist/dom.d.ts +2 -17
- package/dist/dom.js +714 -588
- package/dist/dom.mjs +716 -590
- package/dist/el.cjs +62 -52
- package/dist/el.d.ts +5 -12
- package/dist/el.js +60 -52
- package/dist/el.mjs +60 -52
- package/dist/fs.cjs +72 -63
- package/dist/fs.d.ts +22 -19
- package/dist/fs.js +71 -62
- package/dist/fs.mjs +71 -62
- package/dist/hmr.cjs +40 -14
- package/dist/hmr.d.ts +11 -23
- package/dist/hmr.js +38 -14
- package/dist/hmr.mjs +38 -14
- package/dist/http.cjs +251 -99
- package/dist/http.d.ts +38 -104
- package/dist/http.js +249 -99
- package/dist/http.mjs +249 -99
- package/dist/https.cjs +524 -228
- package/dist/https.d.ts +44 -36
- package/dist/https.js +520 -226
- package/dist/https.mjs +522 -228
- package/dist/index.cjs +7502 -7691
- package/dist/index.d.ts +6 -3
- package/dist/index.js +7486 -7677
- package/dist/index.mjs +7497 -7687
- package/dist/mime-types.cjs +10 -4
- package/dist/mime-types.d.ts +8 -11
- package/dist/mime-types.js +9 -3
- package/dist/mime-types.mjs +9 -3
- package/dist/native.cjs +8616 -8870
- package/dist/native.d.ts +7 -10
- package/dist/native.js +8682 -8936
- package/dist/native.mjs +8615 -8869
- package/dist/path.cjs +83 -77
- package/dist/path.d.ts +29 -29
- package/dist/path.js +82 -76
- package/dist/path.mjs +82 -76
- package/dist/pm.cjs +3300 -0
- package/dist/pm.d.ts +256 -0
- package/dist/pm.js +5638 -0
- package/dist/pm.mjs +3196 -0
- package/dist/preview-build.cjs +712 -0
- package/dist/preview-build.d.ts +59 -0
- package/dist/preview-build.js +3194 -0
- package/dist/preview-build.mjs +676 -0
- package/dist/render-context.cjs +13 -2
- package/dist/render-context.d.ts +9 -34
- package/dist/render-context.js +11 -2
- package/dist/render-context.mjs +11 -2
- package/dist/router.cjs +787 -646
- package/dist/router.d.ts +8 -14
- package/dist/router.js +786 -645
- package/dist/router.mjs +786 -645
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/server.cjs +3254 -2684
- package/dist/server.d.ts +47 -5
- package/dist/server.js +3427 -2859
- package/dist/server.mjs +3397 -2829
- package/dist/smtp-server.cjs +16 -3
- package/dist/smtp-server.d.ts +12 -26
- package/dist/smtp-server.js +18 -5
- package/dist/smtp-server.mjs +16 -3
- package/dist/state-DvEkDehk.d.ts +195 -0
- package/dist/state.cjs +768 -659
- package/dist/state.d.ts +11 -71
- package/dist/state.js +760 -651
- package/dist/state.mjs +767 -658
- package/dist/style.cjs +1011 -968
- package/dist/style.d.ts +13 -127
- package/dist/style.js +1009 -970
- package/dist/style.mjs +1011 -971
- package/dist/test-reporter.cjs +332 -316
- package/dist/test-reporter.d.ts +28 -33
- package/dist/test-reporter.js +328 -312
- package/dist/test-reporter.mjs +328 -312
- package/dist/test-runtime.cjs +927 -968
- package/dist/test-runtime.d.ts +24 -99
- package/dist/test-runtime.js +922 -965
- package/dist/test-runtime.mjs +922 -965
- package/dist/test.cjs +4428 -4273
- package/dist/test.d.ts +2 -8
- package/dist/test.js +4307 -4154
- package/dist/test.mjs +4419 -4267
- package/dist/types-BONVzPtp.d.ts +59 -0
- package/dist/types-BR4wMiVx.d.ts +32 -0
- package/dist/types-C4gKykuG.d.ts +23 -0
- package/dist/types-CIhpN1-K.d.ts +64 -0
- package/dist/types-Ckj8md_j.d.ts +84 -0
- package/dist/types-CpjQTAkX.d.ts +24 -0
- package/dist/types-D0LjrYjS.d.ts +14 -0
- package/dist/types-DAisuVr5.d.ts +75 -0
- package/dist/types-tJn88E1N.d.ts +242 -0
- package/dist/types.d.ts +50 -237
- package/dist/universal.cjs +1 -1
- package/dist/universal.d.ts +1 -7
- package/dist/universal.js +1 -1
- package/dist/universal.mjs +1 -1
- package/dist/websocket-XfyK23zD.d.ts +119 -0
- package/dist/ws.cjs +129 -108
- package/dist/ws.d.ts +21 -131
- package/dist/ws.js +128 -109
- package/dist/ws.mjs +128 -109
- package/dist/wss.cjs +757 -479
- package/dist/wss.d.ts +31 -28
- package/dist/wss.js +755 -479
- package/dist/wss.mjs +758 -482
- package/package.json +8 -1
- package/vendor/epaint-0.31.1/src/image.rs +418 -0
- package/dist/server-uMQvZAll.d.ts +0 -458
package/dist/pm.mjs
ADDED
|
@@ -0,0 +1,3196 @@
|
|
|
1
|
+
import {createRequire as __createRequire} from 'module';const require = __createRequire(import.meta.url);
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
10
|
+
var __esm = (fn, res) => function __init() {
|
|
11
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
|
+
};
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/shares/runtime.ts
|
|
19
|
+
var runtime, isNode, isBun, isDeno;
|
|
20
|
+
var init_runtime = __esm({
|
|
21
|
+
"src/shares/runtime.ts"() {
|
|
22
|
+
"use strict";
|
|
23
|
+
runtime = (() => {
|
|
24
|
+
if (typeof Deno !== "undefined") return "deno";
|
|
25
|
+
if (typeof Bun !== "undefined") return "bun";
|
|
26
|
+
return "node";
|
|
27
|
+
})();
|
|
28
|
+
isNode = runtime === "node";
|
|
29
|
+
isBun = runtime === "bun";
|
|
30
|
+
isDeno = runtime === "deno";
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// src/server/fs/node-modules.ts
|
|
35
|
+
var fs, fsPromises;
|
|
36
|
+
var init_node_modules = __esm({
|
|
37
|
+
"src/server/fs/node-modules.ts"() {
|
|
38
|
+
"use strict";
|
|
39
|
+
init_runtime();
|
|
40
|
+
if (isNode || isBun) {
|
|
41
|
+
fs = __require("fs");
|
|
42
|
+
fsPromises = __require("fs/promises");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// src/server/fs/utils.ts
|
|
48
|
+
function parseOptions(options, defaultValue) {
|
|
49
|
+
return typeof options === "string" ? { encoding: options } : options || defaultValue;
|
|
50
|
+
}
|
|
51
|
+
function decodeContent(content, encoding) {
|
|
52
|
+
if (encoding) {
|
|
53
|
+
return new TextDecoder(encoding).decode(content);
|
|
54
|
+
}
|
|
55
|
+
return Buffer.from(content instanceof ArrayBuffer ? new Uint8Array(content) : content);
|
|
56
|
+
}
|
|
57
|
+
function dataToUint8Array(data) {
|
|
58
|
+
if (typeof data === "string") {
|
|
59
|
+
return new TextEncoder().encode(data);
|
|
60
|
+
}
|
|
61
|
+
if (data instanceof Buffer) {
|
|
62
|
+
return new Uint8Array(data);
|
|
63
|
+
}
|
|
64
|
+
return data;
|
|
65
|
+
}
|
|
66
|
+
function createDirentFromDenoEntry(entry) {
|
|
67
|
+
return {
|
|
68
|
+
name: entry.name,
|
|
69
|
+
isFile: () => entry.isFile,
|
|
70
|
+
isDirectory: () => entry.isDirectory,
|
|
71
|
+
isBlockDevice: () => false,
|
|
72
|
+
isCharacterDevice: () => false,
|
|
73
|
+
isSymbolicLink: () => entry.isSymlink || false,
|
|
74
|
+
isFIFO: () => false,
|
|
75
|
+
isSocket: () => false
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function processDenoEntries(iterator, withFileTypes) {
|
|
79
|
+
const entries = [];
|
|
80
|
+
for (const entry of iterator) {
|
|
81
|
+
if (withFileTypes) {
|
|
82
|
+
entries.push(createDirentFromDenoEntry(entry));
|
|
83
|
+
} else {
|
|
84
|
+
entries.push(entry.name);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return entries;
|
|
88
|
+
}
|
|
89
|
+
async function processDenoEntriesAsync(iterator, withFileTypes) {
|
|
90
|
+
const entries = [];
|
|
91
|
+
for await (const entry of iterator) {
|
|
92
|
+
if (withFileTypes) {
|
|
93
|
+
entries.push(createDirentFromDenoEntry(entry));
|
|
94
|
+
} else {
|
|
95
|
+
entries.push(entry.name);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return entries;
|
|
99
|
+
}
|
|
100
|
+
function createStatsFromDenoFileInfo(info) {
|
|
101
|
+
return {
|
|
102
|
+
isFile: () => info.isFile,
|
|
103
|
+
isDirectory: () => info.isDirectory,
|
|
104
|
+
isBlockDevice: () => false,
|
|
105
|
+
isCharacterDevice: () => false,
|
|
106
|
+
isSymbolicLink: () => info.isSymlink || false,
|
|
107
|
+
isFIFO: () => false,
|
|
108
|
+
isSocket: () => false,
|
|
109
|
+
dev: info.dev || 0,
|
|
110
|
+
ino: info.ino || 0,
|
|
111
|
+
mode: info.mode || 0,
|
|
112
|
+
nlink: info.nlink || 1,
|
|
113
|
+
uid: info.uid || 0,
|
|
114
|
+
gid: info.gid || 0,
|
|
115
|
+
rdev: 0,
|
|
116
|
+
size: info.size,
|
|
117
|
+
blksize: info.blksize || 4096,
|
|
118
|
+
blocks: info.blocks || Math.ceil(info.size / 512),
|
|
119
|
+
atimeMs: info.atime?.getTime() || Date.now(),
|
|
120
|
+
mtimeMs: info.mtime?.getTime() || Date.now(),
|
|
121
|
+
ctimeMs: info.birthtime?.getTime() || Date.now(),
|
|
122
|
+
birthtimeMs: info.birthtime?.getTime() || Date.now(),
|
|
123
|
+
atime: info.atime || /* @__PURE__ */ new Date(),
|
|
124
|
+
mtime: info.mtime || /* @__PURE__ */ new Date(),
|
|
125
|
+
ctime: info.birthtime || /* @__PURE__ */ new Date(),
|
|
126
|
+
birthtime: info.birthtime || /* @__PURE__ */ new Date()
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
var init_utils = __esm({
|
|
130
|
+
"src/server/fs/utils.ts"() {
|
|
131
|
+
"use strict";
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// src/server/fs/file-ops.ts
|
|
136
|
+
async function readFile(path, options) {
|
|
137
|
+
const opts = parseOptions(options, {});
|
|
138
|
+
if (isNode || isBun) {
|
|
139
|
+
return fsPromises.readFile(path, opts);
|
|
140
|
+
} else if (isDeno) {
|
|
141
|
+
const content = await Deno.readFile(path);
|
|
142
|
+
return decodeContent(content, opts.encoding);
|
|
143
|
+
}
|
|
144
|
+
throw new Error("Unsupported runtime");
|
|
145
|
+
}
|
|
146
|
+
function readFileSync2(path, options) {
|
|
147
|
+
const opts = parseOptions(options, {});
|
|
148
|
+
if (isNode || isBun) {
|
|
149
|
+
return fs.readFileSync(path, opts);
|
|
150
|
+
} else if (isDeno) {
|
|
151
|
+
const content = Deno.readFileSync(path);
|
|
152
|
+
return decodeContent(content, opts.encoding);
|
|
153
|
+
}
|
|
154
|
+
throw new Error("Unsupported runtime");
|
|
155
|
+
}
|
|
156
|
+
async function writeFile(path, data, options) {
|
|
157
|
+
const opts = parseOptions(options, {});
|
|
158
|
+
if (isNode || isBun) {
|
|
159
|
+
return fsPromises.writeFile(path, data, opts);
|
|
160
|
+
} else if (isDeno) {
|
|
161
|
+
await Deno.writeFile(path, dataToUint8Array(data));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function writeFileSync2(path, data, options) {
|
|
165
|
+
const opts = parseOptions(options, {});
|
|
166
|
+
if (isNode || isBun) {
|
|
167
|
+
fs.writeFileSync(path, data, opts);
|
|
168
|
+
} else if (isDeno) {
|
|
169
|
+
Deno.writeFileSync(path, dataToUint8Array(data));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function appendFile(path, data, options) {
|
|
173
|
+
const opts = parseOptions(options, {});
|
|
174
|
+
if (isNode) {
|
|
175
|
+
return fsPromises.appendFile(path, data, opts);
|
|
176
|
+
}
|
|
177
|
+
if (await exists(path)) {
|
|
178
|
+
const existing = await readFile(path);
|
|
179
|
+
const combined = Buffer.isBuffer(existing) ? Buffer.concat([existing, Buffer.isBuffer(data) ? data : Buffer.from(data)]) : existing + (Buffer.isBuffer(data) ? data.toString() : data);
|
|
180
|
+
await writeFile(path, combined, opts);
|
|
181
|
+
} else {
|
|
182
|
+
await writeFile(path, data, opts);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function appendFileSync(path, data, options) {
|
|
186
|
+
const opts = parseOptions(options, {});
|
|
187
|
+
if (isNode) {
|
|
188
|
+
fs.appendFileSync(path, data, opts);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (existsSync4(path)) {
|
|
192
|
+
const existing = readFileSync2(path);
|
|
193
|
+
const combined = Buffer.isBuffer(existing) ? Buffer.concat([existing, Buffer.isBuffer(data) ? data : Buffer.from(data)]) : existing + (Buffer.isBuffer(data) ? data.toString() : data);
|
|
194
|
+
writeFileSync2(path, combined, opts);
|
|
195
|
+
} else {
|
|
196
|
+
writeFileSync2(path, data, opts);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function exists(path) {
|
|
200
|
+
try {
|
|
201
|
+
await stat(path);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function existsSync4(path) {
|
|
208
|
+
try {
|
|
209
|
+
statSync2(path);
|
|
210
|
+
return true;
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function stat(path) {
|
|
216
|
+
if (isNode || isBun) {
|
|
217
|
+
return fsPromises.stat(path);
|
|
218
|
+
} else if (isDeno) {
|
|
219
|
+
const info = await Deno.stat(path);
|
|
220
|
+
return createStatsFromDenoFileInfo(info);
|
|
221
|
+
}
|
|
222
|
+
throw new Error("Unsupported runtime");
|
|
223
|
+
}
|
|
224
|
+
function statSync2(path) {
|
|
225
|
+
if (isNode || isBun) {
|
|
226
|
+
return fs.statSync(path);
|
|
227
|
+
} else if (isDeno) {
|
|
228
|
+
const info = Deno.statSync(path);
|
|
229
|
+
return createStatsFromDenoFileInfo(info);
|
|
230
|
+
}
|
|
231
|
+
throw new Error("Unsupported runtime");
|
|
232
|
+
}
|
|
233
|
+
var init_file_ops = __esm({
|
|
234
|
+
"src/server/fs/file-ops.ts"() {
|
|
235
|
+
"use strict";
|
|
236
|
+
init_runtime();
|
|
237
|
+
init_node_modules();
|
|
238
|
+
init_utils();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// src/server/fs/directory-ops.ts
|
|
243
|
+
async function mkdir(path, options) {
|
|
244
|
+
const opts = typeof options === "number" ? { mode: options } : options || {};
|
|
245
|
+
if (isNode || isBun) {
|
|
246
|
+
await fsPromises.mkdir(path, opts);
|
|
247
|
+
} else if (isDeno) {
|
|
248
|
+
await Deno.mkdir(path, { recursive: opts.recursive });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function mkdirSync2(path, options) {
|
|
252
|
+
const opts = typeof options === "number" ? { mode: options } : options || {};
|
|
253
|
+
if (isNode || isBun) {
|
|
254
|
+
fs.mkdirSync(path, opts);
|
|
255
|
+
} else if (isDeno) {
|
|
256
|
+
Deno.mkdirSync(path, { recursive: opts.recursive });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function readdir(path, options) {
|
|
260
|
+
const opts = parseOptions(options, {});
|
|
261
|
+
if (isNode || isBun) {
|
|
262
|
+
return fsPromises.readdir(path, opts);
|
|
263
|
+
} else if (isDeno) {
|
|
264
|
+
return processDenoEntriesAsync(Deno.readDir(path), opts.withFileTypes);
|
|
265
|
+
}
|
|
266
|
+
throw new Error("Unsupported runtime");
|
|
267
|
+
}
|
|
268
|
+
function readdirSync2(path, options) {
|
|
269
|
+
const opts = parseOptions(options, {});
|
|
270
|
+
if (isNode || isBun) {
|
|
271
|
+
return fs.readdirSync(path, opts);
|
|
272
|
+
} else if (isDeno) {
|
|
273
|
+
return processDenoEntries(Deno.readDirSync(path), opts.withFileTypes);
|
|
274
|
+
}
|
|
275
|
+
throw new Error("Unsupported runtime");
|
|
276
|
+
}
|
|
277
|
+
async function unlink(path) {
|
|
278
|
+
if (isNode || isBun) {
|
|
279
|
+
return fsPromises.unlink(path);
|
|
280
|
+
} else if (isDeno) {
|
|
281
|
+
await Deno.remove(path);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function unlinkSync(path) {
|
|
285
|
+
if (isNode || isBun) {
|
|
286
|
+
fs.unlinkSync(path);
|
|
287
|
+
} else if (isDeno) {
|
|
288
|
+
Deno.removeSync(path);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function rmdir(path, options) {
|
|
292
|
+
if (isNode || isBun) {
|
|
293
|
+
return fsPromises.rmdir(path, options);
|
|
294
|
+
} else if (isDeno) {
|
|
295
|
+
await Deno.remove(path, { recursive: options?.recursive });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function rmdirSync(path, options) {
|
|
299
|
+
if (isNode || isBun) {
|
|
300
|
+
fs.rmdirSync(path, options);
|
|
301
|
+
} else if (isDeno) {
|
|
302
|
+
Deno.removeSync(path, { recursive: options?.recursive });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
var init_directory_ops = __esm({
|
|
306
|
+
"src/server/fs/directory-ops.ts"() {
|
|
307
|
+
"use strict";
|
|
308
|
+
init_runtime();
|
|
309
|
+
init_node_modules();
|
|
310
|
+
init_utils();
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// src/server/fs/path-ops.ts
|
|
315
|
+
async function rename(oldPath, newPath) {
|
|
316
|
+
if (isNode || isBun) {
|
|
317
|
+
return fsPromises.rename(oldPath, newPath);
|
|
318
|
+
} else if (isDeno) {
|
|
319
|
+
await Deno.rename(oldPath, newPath);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function renameSync(oldPath, newPath) {
|
|
323
|
+
if (isNode || isBun) {
|
|
324
|
+
fs.renameSync(oldPath, newPath);
|
|
325
|
+
} else if (isDeno) {
|
|
326
|
+
Deno.renameSync(oldPath, newPath);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function copyFile(src, dest, flags) {
|
|
330
|
+
if (isNode || isBun) {
|
|
331
|
+
return fsPromises.copyFile(src, dest, flags);
|
|
332
|
+
} else if (isDeno) {
|
|
333
|
+
await Deno.copyFile(src, dest);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function copyFileSync(src, dest, flags) {
|
|
337
|
+
if (isNode || isBun) {
|
|
338
|
+
fs.copyFileSync(src, dest, flags);
|
|
339
|
+
} else if (isDeno) {
|
|
340
|
+
Deno.copyFileSync(src, dest);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async function realpath(path, options) {
|
|
344
|
+
if (isNode || isBun) {
|
|
345
|
+
return fsPromises.realpath(path, options);
|
|
346
|
+
} else if (isDeno) {
|
|
347
|
+
return await Deno.realPath(path);
|
|
348
|
+
}
|
|
349
|
+
return path;
|
|
350
|
+
}
|
|
351
|
+
function realpathSync(path, options) {
|
|
352
|
+
if (isNode || isBun) {
|
|
353
|
+
return fs.realpathSync(path, options);
|
|
354
|
+
} else if (isDeno) {
|
|
355
|
+
return Deno.realPathSync(path);
|
|
356
|
+
}
|
|
357
|
+
return path;
|
|
358
|
+
}
|
|
359
|
+
var init_path_ops = __esm({
|
|
360
|
+
"src/server/fs/path-ops.ts"() {
|
|
361
|
+
"use strict";
|
|
362
|
+
init_runtime();
|
|
363
|
+
init_node_modules();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// src/server/fs/index.ts
|
|
368
|
+
var fs_exports = {};
|
|
369
|
+
__export(fs_exports, {
|
|
370
|
+
appendFile: () => appendFile,
|
|
371
|
+
appendFileSync: () => appendFileSync,
|
|
372
|
+
copyFile: () => copyFile,
|
|
373
|
+
copyFileSync: () => copyFileSync,
|
|
374
|
+
default: () => fs_default,
|
|
375
|
+
exists: () => exists,
|
|
376
|
+
existsSync: () => existsSync4,
|
|
377
|
+
getRuntime: () => getRuntime,
|
|
378
|
+
mkdir: () => mkdir,
|
|
379
|
+
mkdirSync: () => mkdirSync2,
|
|
380
|
+
promises: () => promises,
|
|
381
|
+
readFile: () => readFile,
|
|
382
|
+
readFileSync: () => readFileSync2,
|
|
383
|
+
readdir: () => readdir,
|
|
384
|
+
readdirSync: () => readdirSync2,
|
|
385
|
+
realpath: () => realpath,
|
|
386
|
+
realpathSync: () => realpathSync,
|
|
387
|
+
rename: () => rename,
|
|
388
|
+
renameSync: () => renameSync,
|
|
389
|
+
rmdir: () => rmdir,
|
|
390
|
+
rmdirSync: () => rmdirSync,
|
|
391
|
+
stat: () => stat,
|
|
392
|
+
statSync: () => statSync2,
|
|
393
|
+
unlink: () => unlink,
|
|
394
|
+
unlinkSync: () => unlinkSync,
|
|
395
|
+
writeFile: () => writeFile,
|
|
396
|
+
writeFileSync: () => writeFileSync2
|
|
397
|
+
});
|
|
398
|
+
function getRuntime() {
|
|
399
|
+
return runtime;
|
|
400
|
+
}
|
|
401
|
+
var promises, fs_default;
|
|
402
|
+
var init_fs = __esm({
|
|
403
|
+
"src/server/fs/index.ts"() {
|
|
404
|
+
"use strict";
|
|
405
|
+
init_runtime();
|
|
406
|
+
init_file_ops();
|
|
407
|
+
init_directory_ops();
|
|
408
|
+
init_path_ops();
|
|
409
|
+
init_file_ops();
|
|
410
|
+
init_directory_ops();
|
|
411
|
+
init_path_ops();
|
|
412
|
+
promises = {
|
|
413
|
+
readFile,
|
|
414
|
+
writeFile,
|
|
415
|
+
appendFile,
|
|
416
|
+
stat,
|
|
417
|
+
mkdir,
|
|
418
|
+
readdir,
|
|
419
|
+
unlink,
|
|
420
|
+
rmdir,
|
|
421
|
+
rename,
|
|
422
|
+
copyFile,
|
|
423
|
+
realpath
|
|
424
|
+
};
|
|
425
|
+
fs_default = {
|
|
426
|
+
readFile,
|
|
427
|
+
readFileSync: readFileSync2,
|
|
428
|
+
writeFile,
|
|
429
|
+
writeFileSync: writeFileSync2,
|
|
430
|
+
appendFile,
|
|
431
|
+
appendFileSync,
|
|
432
|
+
exists,
|
|
433
|
+
existsSync: existsSync4,
|
|
434
|
+
stat,
|
|
435
|
+
statSync: statSync2,
|
|
436
|
+
mkdir,
|
|
437
|
+
mkdirSync: mkdirSync2,
|
|
438
|
+
readdir,
|
|
439
|
+
readdirSync: readdirSync2,
|
|
440
|
+
unlink,
|
|
441
|
+
unlinkSync,
|
|
442
|
+
rmdir,
|
|
443
|
+
rmdirSync,
|
|
444
|
+
rename,
|
|
445
|
+
renameSync,
|
|
446
|
+
copyFile,
|
|
447
|
+
copyFileSync,
|
|
448
|
+
realpath,
|
|
449
|
+
realpathSync,
|
|
450
|
+
promises,
|
|
451
|
+
getRuntime
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// src/cli/pm/shared.ts
|
|
457
|
+
import { join } from "path";
|
|
458
|
+
var DEFAULT_PM_DATA_DIR = join(".elit", "pm");
|
|
459
|
+
var DEFAULT_PM_DUMP_FILE = "dump.json";
|
|
460
|
+
var DEFAULT_RESTART_DELAY = 1e3;
|
|
461
|
+
var DEFAULT_MAX_RESTARTS = 10;
|
|
462
|
+
var DEFAULT_WATCH_DEBOUNCE = 250;
|
|
463
|
+
var DEFAULT_MIN_UPTIME = 0;
|
|
464
|
+
var DEFAULT_HEALTHCHECK_GRACE_PERIOD = 5e3;
|
|
465
|
+
var DEFAULT_HEALTHCHECK_INTERVAL = 1e4;
|
|
466
|
+
var DEFAULT_HEALTHCHECK_TIMEOUT = 3e3;
|
|
467
|
+
var DEFAULT_HEALTHCHECK_MAX_FAILURES = 3;
|
|
468
|
+
var DEFAULT_LOG_LINES = 40;
|
|
469
|
+
var DEFAULT_PM_STOP_POLL_MS = 100;
|
|
470
|
+
var DEFAULT_PM_STOP_GRACE_PERIOD_MS = 5e3;
|
|
471
|
+
var PM_WAPK_ONLINE_STDIN_SHUTDOWN_ENV = "ELIT_PM_WAPK_ONLINE_STDIN_SHUTDOWN";
|
|
472
|
+
var PM_WAPK_ONLINE_SHUTDOWN_COMMAND = "__ELIT_PM_WAPK_ONLINE_SHUTDOWN__";
|
|
473
|
+
var PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS = 8e3;
|
|
474
|
+
var PM_RECORD_EXTENSION = ".json";
|
|
475
|
+
var SUPPORTED_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".mts", ".cts"]);
|
|
476
|
+
var DEFAULT_WATCH_IGNORE = ["**/.git/**", "**/node_modules/**", "**/.elit/**"];
|
|
477
|
+
var SIMPLE_PREVIEW_SEGMENT = /^[A-Za-z0-9_./:=+-]+$/;
|
|
478
|
+
|
|
479
|
+
// src/cli/pm/helpers.ts
|
|
480
|
+
import { existsSync, statSync } from "fs";
|
|
481
|
+
import { extname, join as join2, resolve } from "path";
|
|
482
|
+
function normalizePmRuntime(value, optionName = "--runtime") {
|
|
483
|
+
if (value === void 0 || value === null || value === "") {
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
if (typeof value !== "string") {
|
|
487
|
+
throw new Error(`${optionName} must be one of: node, bun, deno`);
|
|
488
|
+
}
|
|
489
|
+
const runtime2 = value.trim().toLowerCase();
|
|
490
|
+
if (runtime2 === "node" || runtime2 === "bun" || runtime2 === "deno") {
|
|
491
|
+
return runtime2;
|
|
492
|
+
}
|
|
493
|
+
throw new Error(`${optionName} must be one of: node, bun, deno`);
|
|
494
|
+
}
|
|
495
|
+
function normalizePmRestartPolicy(value, optionName = "--restart-policy") {
|
|
496
|
+
if (value === void 0 || value === null || value === "") {
|
|
497
|
+
return void 0;
|
|
498
|
+
}
|
|
499
|
+
if (typeof value !== "string") {
|
|
500
|
+
throw new Error(`${optionName} must be one of: always, on-failure, never`);
|
|
501
|
+
}
|
|
502
|
+
const policy = value.trim().toLowerCase();
|
|
503
|
+
if (policy === "always" || policy === "on-failure" || policy === "never") {
|
|
504
|
+
return policy;
|
|
505
|
+
}
|
|
506
|
+
throw new Error(`${optionName} must be one of: always, on-failure, never`);
|
|
507
|
+
}
|
|
508
|
+
function normalizeIntegerOption(value, optionName, min = 0) {
|
|
509
|
+
const parsed = Number.parseInt(value, 10);
|
|
510
|
+
if (!Number.isFinite(parsed) || parsed < min) {
|
|
511
|
+
throw new Error(`${optionName} must be a number >= ${min}`);
|
|
512
|
+
}
|
|
513
|
+
return parsed;
|
|
514
|
+
}
|
|
515
|
+
function normalizeNonEmptyString(value) {
|
|
516
|
+
if (typeof value !== "string") {
|
|
517
|
+
return void 0;
|
|
518
|
+
}
|
|
519
|
+
const normalized = value.trim();
|
|
520
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
521
|
+
}
|
|
522
|
+
function hasPmGoogleDriveConfig(config) {
|
|
523
|
+
return Boolean(
|
|
524
|
+
normalizeNonEmptyString(config?.fileId) || normalizeNonEmptyString(config?.accessToken) || normalizeNonEmptyString(config?.accessTokenEnv) || typeof config?.supportsAllDrives === "boolean"
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
function isPmWapkOnlineRunConfig(config) {
|
|
528
|
+
return Boolean(config?.online || normalizeNonEmptyString(config?.onlineUrl));
|
|
529
|
+
}
|
|
530
|
+
function hasPmWapkRunConfig(config) {
|
|
531
|
+
return Boolean(
|
|
532
|
+
normalizeNonEmptyString(config?.file) || hasPmGoogleDriveConfig(config?.googleDrive) || isPmWapkOnlineRunConfig(config) || normalizeNonEmptyString(config?.runtime) || typeof config?.syncInterval === "number" || typeof config?.useWatcher === "boolean" || typeof config?.watchArchive === "boolean" || typeof config?.archiveSyncInterval === "number" || normalizeNonEmptyString(config?.password)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
function mergePmWapkRunConfig(base, override) {
|
|
536
|
+
if (!base && !override) {
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
539
|
+
const googleDrive = hasPmGoogleDriveConfig(base?.googleDrive) || hasPmGoogleDriveConfig(override?.googleDrive) ? {
|
|
540
|
+
fileId: override?.googleDrive?.fileId ?? base?.googleDrive?.fileId,
|
|
541
|
+
accessToken: override?.googleDrive?.accessToken ?? base?.googleDrive?.accessToken,
|
|
542
|
+
accessTokenEnv: override?.googleDrive?.accessTokenEnv ?? base?.googleDrive?.accessTokenEnv,
|
|
543
|
+
supportsAllDrives: override?.googleDrive?.supportsAllDrives ?? base?.googleDrive?.supportsAllDrives
|
|
544
|
+
} : void 0;
|
|
545
|
+
const merged = {
|
|
546
|
+
file: override?.file ?? base?.file,
|
|
547
|
+
googleDrive,
|
|
548
|
+
online: override?.online ?? base?.online,
|
|
549
|
+
onlineUrl: override?.onlineUrl ?? base?.onlineUrl,
|
|
550
|
+
runtime: override?.runtime ?? base?.runtime,
|
|
551
|
+
syncInterval: override?.syncInterval ?? base?.syncInterval,
|
|
552
|
+
useWatcher: override?.useWatcher ?? base?.useWatcher,
|
|
553
|
+
watchArchive: override?.watchArchive ?? base?.watchArchive,
|
|
554
|
+
archiveSyncInterval: override?.archiveSyncInterval ?? base?.archiveSyncInterval,
|
|
555
|
+
password: override?.password ?? base?.password
|
|
556
|
+
};
|
|
557
|
+
return hasPmWapkRunConfig(merged) ? merged : void 0;
|
|
558
|
+
}
|
|
559
|
+
function stripPmWapkSourceFromRunConfig(config) {
|
|
560
|
+
if (!config) {
|
|
561
|
+
return void 0;
|
|
562
|
+
}
|
|
563
|
+
const googleDrive = hasPmGoogleDriveConfig({
|
|
564
|
+
...config.googleDrive,
|
|
565
|
+
fileId: void 0
|
|
566
|
+
}) ? {
|
|
567
|
+
...config.googleDrive,
|
|
568
|
+
fileId: void 0
|
|
569
|
+
} : void 0;
|
|
570
|
+
const stripped = {
|
|
571
|
+
file: void 0,
|
|
572
|
+
googleDrive,
|
|
573
|
+
online: config.online,
|
|
574
|
+
onlineUrl: config.onlineUrl,
|
|
575
|
+
runtime: void 0,
|
|
576
|
+
syncInterval: config.syncInterval,
|
|
577
|
+
useWatcher: config.useWatcher,
|
|
578
|
+
watchArchive: config.watchArchive,
|
|
579
|
+
archiveSyncInterval: config.archiveSyncInterval,
|
|
580
|
+
password: void 0
|
|
581
|
+
};
|
|
582
|
+
return hasPmWapkRunConfig(stripped) ? stripped : void 0;
|
|
583
|
+
}
|
|
584
|
+
function isRemoteWapkArchiveSpecifier(value) {
|
|
585
|
+
return /^(?:gdrive|google-drive):\/\/.+/i.test(value.trim());
|
|
586
|
+
}
|
|
587
|
+
function isWapkArchiveSpecifier(value) {
|
|
588
|
+
const normalized = value.trim();
|
|
589
|
+
return normalized.toLowerCase().endsWith(".wapk") || isRemoteWapkArchiveSpecifier(normalized);
|
|
590
|
+
}
|
|
591
|
+
function buildGoogleDriveWapkSpecifier(fileId) {
|
|
592
|
+
return `gdrive://${fileId}`;
|
|
593
|
+
}
|
|
594
|
+
function resolvePmWapkSource(value, cwd) {
|
|
595
|
+
const normalized = normalizeNonEmptyString(value);
|
|
596
|
+
if (!normalized) {
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
return isRemoteWapkArchiveSpecifier(normalized) ? normalized : resolve(cwd, normalized);
|
|
600
|
+
}
|
|
601
|
+
function resolvePmWapkSourceToken(wapk, wapkRun) {
|
|
602
|
+
const googleDriveFileId = normalizeNonEmptyString(wapkRun?.googleDrive?.fileId);
|
|
603
|
+
return normalizeNonEmptyString(wapk) ?? normalizeNonEmptyString(wapkRun?.file) ?? (googleDriveFileId ? buildGoogleDriveWapkSpecifier(googleDriveFileId) : void 0);
|
|
604
|
+
}
|
|
605
|
+
function countDefinedPmWapkSources(wapk, wapkRun) {
|
|
606
|
+
const values = [
|
|
607
|
+
normalizeNonEmptyString(wapk),
|
|
608
|
+
normalizeNonEmptyString(wapkRun?.file),
|
|
609
|
+
normalizeNonEmptyString(wapkRun?.googleDrive?.fileId)
|
|
610
|
+
].filter((entry) => Boolean(entry));
|
|
611
|
+
return new Set(values).size;
|
|
612
|
+
}
|
|
613
|
+
function appendPmWapkRunArgs(args, previewParts, wapkRun) {
|
|
614
|
+
if (!wapkRun) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
if (isPmWapkOnlineRunConfig(wapkRun)) {
|
|
618
|
+
args.push("--online");
|
|
619
|
+
previewParts.push("--online");
|
|
620
|
+
}
|
|
621
|
+
const onlineUrl = normalizeNonEmptyString(wapkRun.onlineUrl);
|
|
622
|
+
if (onlineUrl) {
|
|
623
|
+
args.push("--online-url", onlineUrl);
|
|
624
|
+
previewParts.push("--online-url", onlineUrl);
|
|
625
|
+
}
|
|
626
|
+
if (typeof wapkRun.syncInterval === "number" && Number.isFinite(wapkRun.syncInterval) && wapkRun.syncInterval >= 50) {
|
|
627
|
+
const value = String(Math.trunc(wapkRun.syncInterval));
|
|
628
|
+
args.push("--sync-interval", value);
|
|
629
|
+
previewParts.push("--sync-interval", value);
|
|
630
|
+
}
|
|
631
|
+
if (wapkRun.useWatcher) {
|
|
632
|
+
args.push("--watcher");
|
|
633
|
+
previewParts.push("--watcher");
|
|
634
|
+
}
|
|
635
|
+
if (typeof wapkRun.watchArchive === "boolean") {
|
|
636
|
+
const flag = wapkRun.watchArchive ? "--archive-watch" : "--no-archive-watch";
|
|
637
|
+
args.push(flag);
|
|
638
|
+
previewParts.push(flag);
|
|
639
|
+
}
|
|
640
|
+
if (typeof wapkRun.archiveSyncInterval === "number" && Number.isFinite(wapkRun.archiveSyncInterval) && wapkRun.archiveSyncInterval >= 50) {
|
|
641
|
+
const value = String(Math.trunc(wapkRun.archiveSyncInterval));
|
|
642
|
+
args.push("--archive-sync-interval", value);
|
|
643
|
+
previewParts.push("--archive-sync-interval", value);
|
|
644
|
+
}
|
|
645
|
+
const tokenEnv = normalizeNonEmptyString(wapkRun.googleDrive?.accessTokenEnv);
|
|
646
|
+
if (tokenEnv) {
|
|
647
|
+
args.push("--google-drive-token-env", tokenEnv);
|
|
648
|
+
previewParts.push("--google-drive-token-env", tokenEnv);
|
|
649
|
+
}
|
|
650
|
+
const accessToken = normalizeNonEmptyString(wapkRun.googleDrive?.accessToken);
|
|
651
|
+
if (accessToken) {
|
|
652
|
+
args.push("--google-drive-access-token", accessToken);
|
|
653
|
+
previewParts.push("--google-drive-access-token", "******");
|
|
654
|
+
}
|
|
655
|
+
if (wapkRun.googleDrive?.supportsAllDrives) {
|
|
656
|
+
args.push("--google-drive-shared-drive");
|
|
657
|
+
previewParts.push("--google-drive-shared-drive");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function quoteCommandSegment(value) {
|
|
661
|
+
return SIMPLE_PREVIEW_SEGMENT.test(value) ? value : JSON.stringify(value);
|
|
662
|
+
}
|
|
663
|
+
function buildPmWapkPreview(wapk, runtime2, password, wapkRun) {
|
|
664
|
+
const previewParts = ["elit", "wapk", "run", quoteCommandSegment(wapk)];
|
|
665
|
+
const online = isPmWapkOnlineRunConfig(wapkRun);
|
|
666
|
+
if (runtime2 && !online) {
|
|
667
|
+
previewParts.push("--runtime", runtime2);
|
|
668
|
+
}
|
|
669
|
+
if (password) {
|
|
670
|
+
previewParts.push("--password", "******");
|
|
671
|
+
}
|
|
672
|
+
appendPmWapkRunArgs([], previewParts, wapkRun);
|
|
673
|
+
return previewParts.join(" ");
|
|
674
|
+
}
|
|
675
|
+
function sanitizePmProcessName(name) {
|
|
676
|
+
const sanitized = name.trim().toLowerCase().replace(/[<>:"/\\|?*\x00-\x1f]+/g, "-").replace(/\s+/g, "-").replace(/-+/g, "-");
|
|
677
|
+
return sanitized.length > 0 ? sanitized : "process";
|
|
678
|
+
}
|
|
679
|
+
function isTypescriptFile(filePath) {
|
|
680
|
+
const extension = extname(filePath).toLowerCase();
|
|
681
|
+
return extension === ".ts" || extension === ".mts" || extension === ".cts";
|
|
682
|
+
}
|
|
683
|
+
function normalizeStringArray(value) {
|
|
684
|
+
if (!Array.isArray(value)) {
|
|
685
|
+
return [];
|
|
686
|
+
}
|
|
687
|
+
return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
688
|
+
}
|
|
689
|
+
function toWatchGlob(candidatePath) {
|
|
690
|
+
if (!existsSync(candidatePath)) {
|
|
691
|
+
return candidatePath;
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
return statSync(candidatePath).isDirectory() ? join2(candidatePath, "**", "*").replace(/\\/g, "/") : candidatePath;
|
|
695
|
+
} catch {
|
|
696
|
+
return candidatePath;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function normalizeWatchPatterns(paths, cwd) {
|
|
700
|
+
return paths.map((entry) => resolve(cwd, entry)).map(toWatchGlob).map((entry) => entry.replace(/\\/g, "/"));
|
|
701
|
+
}
|
|
702
|
+
function normalizeWatchIgnorePatterns(paths, cwd) {
|
|
703
|
+
return paths.map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => {
|
|
704
|
+
if (entry.includes("*") || entry.includes("?")) {
|
|
705
|
+
return entry.replace(/\\/g, "/");
|
|
706
|
+
}
|
|
707
|
+
const resolvedPath = resolve(cwd, entry);
|
|
708
|
+
return toWatchGlob(resolvedPath).replace(/\\/g, "/");
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
function matchesGlobPattern(filePath, pattern) {
|
|
712
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
713
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
714
|
+
const regexPattern = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, ".");
|
|
715
|
+
return new RegExp(`^${regexPattern}$`).test(normalizedPath);
|
|
716
|
+
}
|
|
717
|
+
function isIgnoredWatchPath(filePath, patterns) {
|
|
718
|
+
return patterns.some((pattern) => matchesGlobPattern(filePath, pattern));
|
|
719
|
+
}
|
|
720
|
+
function normalizeHealthCheckConfig(value) {
|
|
721
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
722
|
+
return void 0;
|
|
723
|
+
}
|
|
724
|
+
const config = value;
|
|
725
|
+
if (typeof config.url !== "string" || config.url.trim().length === 0) {
|
|
726
|
+
return void 0;
|
|
727
|
+
}
|
|
728
|
+
return {
|
|
729
|
+
url: config.url.trim(),
|
|
730
|
+
gracePeriod: typeof config.gracePeriod === "number" && Number.isFinite(config.gracePeriod) ? Math.max(0, Math.trunc(config.gracePeriod)) : DEFAULT_HEALTHCHECK_GRACE_PERIOD,
|
|
731
|
+
interval: typeof config.interval === "number" && Number.isFinite(config.interval) ? Math.max(250, Math.trunc(config.interval)) : DEFAULT_HEALTHCHECK_INTERVAL,
|
|
732
|
+
timeout: typeof config.timeout === "number" && Number.isFinite(config.timeout) ? Math.max(250, Math.trunc(config.timeout)) : DEFAULT_HEALTHCHECK_TIMEOUT,
|
|
733
|
+
maxFailures: typeof config.maxFailures === "number" && Number.isFinite(config.maxFailures) ? Math.max(1, Math.trunc(config.maxFailures)) : DEFAULT_HEALTHCHECK_MAX_FAILURES
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
function looksLikeManagedFile(value, cwd) {
|
|
737
|
+
const normalized = value.trim();
|
|
738
|
+
if (!normalized) {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
if (isRemoteWapkArchiveSpecifier(normalized)) {
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
if (normalized.toLowerCase().endsWith(".wapk")) {
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
const extension = extname(normalized).toLowerCase();
|
|
748
|
+
if (SUPPORTED_FILE_EXTENSIONS.has(extension)) {
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
if (normalized.includes("/") || normalized.includes("\\") || normalized.startsWith(".")) {
|
|
752
|
+
return existsSync(resolve(cwd, normalized));
|
|
753
|
+
}
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
function normalizeEnvMap(value) {
|
|
757
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
const normalized = {};
|
|
761
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
762
|
+
if (typeof entryValue === "string") {
|
|
763
|
+
normalized[key] = entryValue;
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (typeof entryValue === "number" || typeof entryValue === "boolean") {
|
|
767
|
+
normalized[key] = String(entryValue);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return normalized;
|
|
771
|
+
}
|
|
772
|
+
function parsePmEnvEntry(input) {
|
|
773
|
+
const separatorIndex = input.indexOf("=");
|
|
774
|
+
if (separatorIndex <= 0) {
|
|
775
|
+
throw new Error("--env expects KEY=VALUE");
|
|
776
|
+
}
|
|
777
|
+
const key = input.slice(0, separatorIndex).trim();
|
|
778
|
+
const value = input.slice(separatorIndex + 1);
|
|
779
|
+
if (!key) {
|
|
780
|
+
throw new Error("--env expects KEY=VALUE");
|
|
781
|
+
}
|
|
782
|
+
return [key, value];
|
|
783
|
+
}
|
|
784
|
+
function readRequiredValue(args, index, optionName) {
|
|
785
|
+
const value = args[index];
|
|
786
|
+
if (value === void 0) {
|
|
787
|
+
throw new Error(`${optionName} requires a value.`);
|
|
788
|
+
}
|
|
789
|
+
return value;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// src/cli/pm/records.ts
|
|
793
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
|
|
794
|
+
import { dirname, join as join3, resolve as resolve2 } from "path";
|
|
795
|
+
function resolvePmPaths(config, workspaceRoot) {
|
|
796
|
+
const dataDir = resolve2(workspaceRoot, config?.dataDir ?? DEFAULT_PM_DATA_DIR);
|
|
797
|
+
const dumpFile = config?.dumpFile ? resolve2(workspaceRoot, config.dumpFile) : join3(dataDir, DEFAULT_PM_DUMP_FILE);
|
|
798
|
+
return {
|
|
799
|
+
dataDir,
|
|
800
|
+
appsDir: join3(dataDir, "apps"),
|
|
801
|
+
logsDir: join3(dataDir, "logs"),
|
|
802
|
+
dumpFile
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function ensurePmDirectories(paths) {
|
|
806
|
+
mkdirSync(paths.dataDir, { recursive: true });
|
|
807
|
+
mkdirSync(paths.appsDir, { recursive: true });
|
|
808
|
+
mkdirSync(paths.logsDir, { recursive: true });
|
|
809
|
+
mkdirSync(dirname(paths.dumpFile), { recursive: true });
|
|
810
|
+
}
|
|
811
|
+
function getPmRecordPath(paths, id) {
|
|
812
|
+
return join3(paths.appsDir, `${id}${PM_RECORD_EXTENSION}`);
|
|
813
|
+
}
|
|
814
|
+
function readPmRecord(filePath) {
|
|
815
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
816
|
+
}
|
|
817
|
+
function writePmRecord(filePath, record) {
|
|
818
|
+
writeFileSync(filePath, JSON.stringify(record, null, 2));
|
|
819
|
+
}
|
|
820
|
+
function toSavedAppDefinition(record) {
|
|
821
|
+
return {
|
|
822
|
+
name: record.name,
|
|
823
|
+
type: record.type,
|
|
824
|
+
cwd: record.cwd,
|
|
825
|
+
runtime: record.runtime,
|
|
826
|
+
env: record.env,
|
|
827
|
+
script: record.script,
|
|
828
|
+
file: record.file,
|
|
829
|
+
wapk: record.wapk,
|
|
830
|
+
password: record.password,
|
|
831
|
+
wapkRun: record.wapkRun,
|
|
832
|
+
restartPolicy: record.restartPolicy,
|
|
833
|
+
autorestart: record.autorestart,
|
|
834
|
+
restartDelay: record.restartDelay,
|
|
835
|
+
maxRestarts: record.maxRestarts,
|
|
836
|
+
minUptime: record.minUptime,
|
|
837
|
+
watch: record.watch,
|
|
838
|
+
watchPaths: record.watchPaths,
|
|
839
|
+
watchIgnore: record.watchIgnore,
|
|
840
|
+
watchDebounce: record.watchDebounce,
|
|
841
|
+
healthCheck: record.healthCheck
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
function writePmDumpFile(filePath, apps) {
|
|
845
|
+
const dump = {
|
|
846
|
+
version: 1,
|
|
847
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
848
|
+
apps
|
|
849
|
+
};
|
|
850
|
+
writeFileSync(filePath, JSON.stringify(dump, null, 2));
|
|
851
|
+
}
|
|
852
|
+
function readPmDumpFile(filePath) {
|
|
853
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
854
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.apps)) {
|
|
855
|
+
throw new Error(`Invalid PM dump file: ${filePath}`);
|
|
856
|
+
}
|
|
857
|
+
return {
|
|
858
|
+
version: 1,
|
|
859
|
+
savedAt: typeof parsed.savedAt === "string" ? parsed.savedAt : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
860
|
+
apps: parsed.apps
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function deriveDefaultWatchPaths(type, cwd, file, wapk) {
|
|
864
|
+
if (type === "file" && file) {
|
|
865
|
+
return [file];
|
|
866
|
+
}
|
|
867
|
+
if (type === "wapk" && wapk) {
|
|
868
|
+
return [isRemoteWapkArchiveSpecifier(wapk) ? cwd : wapk];
|
|
869
|
+
}
|
|
870
|
+
return [cwd];
|
|
871
|
+
}
|
|
872
|
+
function normalizeResolvedWatchPaths(paths, cwd, type, file, wapk) {
|
|
873
|
+
const sourcePaths = paths.length > 0 ? paths : deriveDefaultWatchPaths(type, cwd, file, wapk);
|
|
874
|
+
return normalizeWatchPatterns(sourcePaths, cwd);
|
|
875
|
+
}
|
|
876
|
+
function normalizeResolvedWatchIgnorePaths(paths, cwd) {
|
|
877
|
+
return normalizeWatchIgnorePatterns(paths, cwd);
|
|
878
|
+
}
|
|
879
|
+
function listPmRecordMatches(paths) {
|
|
880
|
+
if (!existsSync2(paths.appsDir)) {
|
|
881
|
+
return [];
|
|
882
|
+
}
|
|
883
|
+
return readdirSync(paths.appsDir).filter((entry) => entry.endsWith(PM_RECORD_EXTENSION)).map((entry) => {
|
|
884
|
+
const filePath = join3(paths.appsDir, entry);
|
|
885
|
+
return {
|
|
886
|
+
filePath,
|
|
887
|
+
record: readPmRecord(filePath)
|
|
888
|
+
};
|
|
889
|
+
}).sort((left, right) => left.record.name.localeCompare(right.record.name));
|
|
890
|
+
}
|
|
891
|
+
function findPmRecordMatch(paths, nameOrId) {
|
|
892
|
+
const directPath = getPmRecordPath(paths, sanitizePmProcessName(nameOrId));
|
|
893
|
+
if (existsSync2(directPath)) {
|
|
894
|
+
return {
|
|
895
|
+
filePath: directPath,
|
|
896
|
+
record: readPmRecord(directPath)
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
return listPmRecordMatches(paths).find((match) => match.record.name === nameOrId);
|
|
900
|
+
}
|
|
901
|
+
function isProcessAlive(pid) {
|
|
902
|
+
if (!pid || pid <= 0) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
try {
|
|
906
|
+
process.kill(pid, 0);
|
|
907
|
+
return true;
|
|
908
|
+
} catch (error) {
|
|
909
|
+
const code = error.code;
|
|
910
|
+
return code === "EPERM";
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function syncPmRecordLiveness(match) {
|
|
914
|
+
const { record } = match;
|
|
915
|
+
if (record.desiredState === "running" && record.runnerPid && !isProcessAlive(record.runnerPid)) {
|
|
916
|
+
const updated = {
|
|
917
|
+
...record,
|
|
918
|
+
status: record.status === "stopping" ? "stopped" : record.status === "errored" ? "errored" : "exited",
|
|
919
|
+
runnerPid: void 0,
|
|
920
|
+
childPid: void 0,
|
|
921
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
922
|
+
};
|
|
923
|
+
writePmRecord(match.filePath, updated);
|
|
924
|
+
return { ...match, record: updated };
|
|
925
|
+
}
|
|
926
|
+
if (record.childPid && !isProcessAlive(record.childPid)) {
|
|
927
|
+
const updated = {
|
|
928
|
+
...record,
|
|
929
|
+
childPid: void 0,
|
|
930
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
931
|
+
};
|
|
932
|
+
writePmRecord(match.filePath, updated);
|
|
933
|
+
return { ...match, record: updated };
|
|
934
|
+
}
|
|
935
|
+
return match;
|
|
936
|
+
}
|
|
937
|
+
function readLatestPmRecord(filePath, fallback) {
|
|
938
|
+
return existsSync2(filePath) ? readPmRecord(filePath) : fallback;
|
|
939
|
+
}
|
|
940
|
+
function toPmAppConfig(record) {
|
|
941
|
+
return {
|
|
942
|
+
name: record.name,
|
|
943
|
+
script: record.script,
|
|
944
|
+
file: record.file,
|
|
945
|
+
wapk: record.wapk,
|
|
946
|
+
wapkRun: record.wapkRun,
|
|
947
|
+
runtime: record.runtime,
|
|
948
|
+
cwd: record.cwd,
|
|
949
|
+
env: record.env,
|
|
950
|
+
autorestart: record.autorestart,
|
|
951
|
+
restartDelay: record.restartDelay,
|
|
952
|
+
maxRestarts: record.maxRestarts,
|
|
953
|
+
password: record.password,
|
|
954
|
+
restartPolicy: record.restartPolicy,
|
|
955
|
+
minUptime: record.minUptime,
|
|
956
|
+
watch: record.watch,
|
|
957
|
+
watchPaths: record.watchPaths,
|
|
958
|
+
watchIgnore: record.watchIgnore,
|
|
959
|
+
watchDebounce: record.watchDebounce,
|
|
960
|
+
healthCheck: record.healthCheck
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
function toSavedPmAppConfig(app) {
|
|
964
|
+
return {
|
|
965
|
+
name: app.name,
|
|
966
|
+
script: app.script,
|
|
967
|
+
file: app.file,
|
|
968
|
+
wapk: app.wapk,
|
|
969
|
+
wapkRun: app.wapkRun,
|
|
970
|
+
runtime: app.runtime,
|
|
971
|
+
cwd: app.cwd,
|
|
972
|
+
env: app.env,
|
|
973
|
+
autorestart: app.autorestart,
|
|
974
|
+
restartDelay: app.restartDelay,
|
|
975
|
+
maxRestarts: app.maxRestarts,
|
|
976
|
+
password: app.password,
|
|
977
|
+
restartPolicy: app.restartPolicy,
|
|
978
|
+
minUptime: app.minUptime,
|
|
979
|
+
watch: app.watch,
|
|
980
|
+
watchPaths: app.watchPaths,
|
|
981
|
+
watchIgnore: app.watchIgnore,
|
|
982
|
+
watchDebounce: app.watchDebounce,
|
|
983
|
+
healthCheck: app.healthCheck
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// src/cli/pm/config.ts
|
|
988
|
+
import { resolve as resolve3 } from "path";
|
|
989
|
+
function parsePmTarget(parsed, workspaceRoot) {
|
|
990
|
+
if (parsed.script) {
|
|
991
|
+
return { script: parsed.script };
|
|
992
|
+
}
|
|
993
|
+
if (parsed.file) {
|
|
994
|
+
return { file: parsed.file };
|
|
995
|
+
}
|
|
996
|
+
if (parsed.wapk) {
|
|
997
|
+
return { wapk: parsed.wapk };
|
|
998
|
+
}
|
|
999
|
+
if (!parsed.targetToken) {
|
|
1000
|
+
return {};
|
|
1001
|
+
}
|
|
1002
|
+
if (isWapkArchiveSpecifier(parsed.targetToken)) {
|
|
1003
|
+
return { wapk: parsed.targetToken };
|
|
1004
|
+
}
|
|
1005
|
+
if (looksLikeManagedFile(parsed.targetToken, resolve3(workspaceRoot, parsed.cwd ?? "."))) {
|
|
1006
|
+
return { file: parsed.targetToken };
|
|
1007
|
+
}
|
|
1008
|
+
return { configName: parsed.targetToken };
|
|
1009
|
+
}
|
|
1010
|
+
function getConfiguredPmApps(config) {
|
|
1011
|
+
return Array.isArray(config?.pm?.apps) ? config.pm.apps : [];
|
|
1012
|
+
}
|
|
1013
|
+
function defaultProcessName(base, explicitName) {
|
|
1014
|
+
if (explicitName && explicitName.trim()) {
|
|
1015
|
+
return explicitName.trim();
|
|
1016
|
+
}
|
|
1017
|
+
if (base.file) {
|
|
1018
|
+
const fileName = base.file.split(/[\\/]/).pop()?.replace(/\.[^.]+$/, "");
|
|
1019
|
+
return fileName || "process";
|
|
1020
|
+
}
|
|
1021
|
+
if (base.wapk) {
|
|
1022
|
+
const fileName = base.wapk.split(/[\\/]/).pop()?.replace(/\.[^.]+$/, "");
|
|
1023
|
+
return fileName || "wapk-app";
|
|
1024
|
+
}
|
|
1025
|
+
if (base.script) {
|
|
1026
|
+
const candidate = base.script.trim().split(/\s+/).slice(0, 2).join("-");
|
|
1027
|
+
return candidate || "process";
|
|
1028
|
+
}
|
|
1029
|
+
return "process";
|
|
1030
|
+
}
|
|
1031
|
+
function countDefinedTargets(app) {
|
|
1032
|
+
return [app.script, app.file, app.wapk].filter(Boolean).length;
|
|
1033
|
+
}
|
|
1034
|
+
function resolveStartSelection(configApps, parsed, workspaceRoot) {
|
|
1035
|
+
const target = parsePmTarget(parsed, workspaceRoot);
|
|
1036
|
+
const hasExplicitWapkSource = Boolean(resolvePmWapkSourceToken(parsed.wapk, parsed.wapkRun));
|
|
1037
|
+
const selectedName = target.configName ?? (!target.script && !target.file && !target.wapk && !hasExplicitWapkSource ? parsed.name : void 0);
|
|
1038
|
+
const selected = selectedName ? configApps.find((app) => app.name === selectedName) : void 0;
|
|
1039
|
+
return {
|
|
1040
|
+
selected,
|
|
1041
|
+
startAll: !target.script && !target.file && !target.wapk && !hasExplicitWapkSource && !selectedName,
|
|
1042
|
+
target
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function resolvePmAppDefinition(base, parsed, workspaceRoot, source) {
|
|
1046
|
+
const target = parsePmTarget(parsed, workspaceRoot);
|
|
1047
|
+
const resolvedCwd = resolve3(workspaceRoot, parsed.cwd ?? base?.cwd ?? ".");
|
|
1048
|
+
if (countDefinedPmWapkSources(parsed.wapk, parsed.wapkRun) > 1) {
|
|
1049
|
+
throw new Error("Use only one WAPK archive source per pm start: --wapk or --google-drive-file-id.");
|
|
1050
|
+
}
|
|
1051
|
+
if (countDefinedPmWapkSources(base?.wapk, base?.wapkRun) > 1) {
|
|
1052
|
+
throw new Error(`Configured pm app "${base?.name ?? parsed.name ?? "app"}" must define only one WAPK archive source.`);
|
|
1053
|
+
}
|
|
1054
|
+
const explicitWapk = resolvePmWapkSource(resolvePmWapkSourceToken(target.wapk, parsed.wapkRun), resolvedCwd);
|
|
1055
|
+
const baseWapk = resolvePmWapkSource(resolvePmWapkSourceToken(base?.wapk, base?.wapkRun), resolvedCwd);
|
|
1056
|
+
const hasExplicitTarget = Boolean(target.script || target.file || explicitWapk);
|
|
1057
|
+
const script = target.script ?? (hasExplicitTarget ? void 0 : base?.script);
|
|
1058
|
+
const file = target.file ? resolve3(resolvedCwd, target.file) : hasExplicitTarget ? void 0 : base?.file ? resolve3(resolvedCwd, base.file) : void 0;
|
|
1059
|
+
const wapk = explicitWapk ?? (hasExplicitTarget ? void 0 : baseWapk);
|
|
1060
|
+
const targetCount = countDefinedTargets({ script, file, wapk });
|
|
1061
|
+
if (targetCount === 0) {
|
|
1062
|
+
throw new Error("pm start requires one target: --script, --file, --wapk, or a configured app name.");
|
|
1063
|
+
}
|
|
1064
|
+
if (targetCount > 1) {
|
|
1065
|
+
throw new Error("A pm app must define exactly one of script, file, or wapk.");
|
|
1066
|
+
}
|
|
1067
|
+
const name = defaultProcessName({ script, file, wapk }, parsed.name ?? base?.name);
|
|
1068
|
+
const mergedWapkRun = mergePmWapkRunConfig(base?.wapkRun, parsed.wapkRun);
|
|
1069
|
+
const runtime2 = normalizePmRuntime(parsed.runtime ?? mergedWapkRun?.runtime ?? base?.runtime, "--runtime");
|
|
1070
|
+
let restartPolicy = normalizePmRestartPolicy(parsed.restartPolicy ?? base?.restartPolicy, "--restart-policy") ?? (base?.autorestart ?? true ? "always" : "never");
|
|
1071
|
+
if (parsed.autorestart === false) {
|
|
1072
|
+
restartPolicy = "never";
|
|
1073
|
+
}
|
|
1074
|
+
const autorestart = restartPolicy !== "never";
|
|
1075
|
+
const watch2 = parsed.watch ?? base?.watch ?? false;
|
|
1076
|
+
const configuredWatchPaths = parsed.watchPaths.length > 0 ? parsed.watchPaths : normalizeStringArray(base?.watchPaths);
|
|
1077
|
+
const configuredWatchIgnore = [
|
|
1078
|
+
...DEFAULT_WATCH_IGNORE,
|
|
1079
|
+
...normalizeStringArray(base?.watchIgnore),
|
|
1080
|
+
...parsed.watchIgnore
|
|
1081
|
+
];
|
|
1082
|
+
const healthCheck = normalizeHealthCheckConfig(parsed.healthCheckUrl ? {
|
|
1083
|
+
url: parsed.healthCheckUrl,
|
|
1084
|
+
gracePeriod: parsed.healthCheckGracePeriod,
|
|
1085
|
+
interval: parsed.healthCheckInterval,
|
|
1086
|
+
timeout: parsed.healthCheckTimeout,
|
|
1087
|
+
maxFailures: parsed.healthCheckMaxFailures
|
|
1088
|
+
} : base?.healthCheck);
|
|
1089
|
+
const password = parsed.password ?? mergedWapkRun?.password ?? base?.password;
|
|
1090
|
+
const wapkRun = stripPmWapkSourceFromRunConfig(mergedWapkRun);
|
|
1091
|
+
if (password && !wapk) {
|
|
1092
|
+
throw new Error("--password is only supported when starting a WAPK app.");
|
|
1093
|
+
}
|
|
1094
|
+
if (wapkRun && !wapk) {
|
|
1095
|
+
throw new Error("WAPK run options are only supported when starting a WAPK app.");
|
|
1096
|
+
}
|
|
1097
|
+
return {
|
|
1098
|
+
name,
|
|
1099
|
+
type: script ? "script" : wapk ? "wapk" : "file",
|
|
1100
|
+
source,
|
|
1101
|
+
cwd: resolvedCwd,
|
|
1102
|
+
runtime: runtime2,
|
|
1103
|
+
env: {
|
|
1104
|
+
...normalizeEnvMap(base?.env),
|
|
1105
|
+
...parsed.env
|
|
1106
|
+
},
|
|
1107
|
+
script,
|
|
1108
|
+
file,
|
|
1109
|
+
wapk,
|
|
1110
|
+
wapkRun,
|
|
1111
|
+
autorestart,
|
|
1112
|
+
restartDelay: parsed.restartDelay ?? base?.restartDelay ?? DEFAULT_RESTART_DELAY,
|
|
1113
|
+
maxRestarts: parsed.maxRestarts ?? base?.maxRestarts ?? DEFAULT_MAX_RESTARTS,
|
|
1114
|
+
password,
|
|
1115
|
+
restartPolicy,
|
|
1116
|
+
minUptime: parsed.minUptime ?? base?.minUptime ?? DEFAULT_MIN_UPTIME,
|
|
1117
|
+
watch: watch2,
|
|
1118
|
+
watchPaths: watch2 ? normalizeResolvedWatchPaths(configuredWatchPaths, resolvedCwd, script ? "script" : wapk ? "wapk" : "file", file, wapk) : [],
|
|
1119
|
+
watchIgnore: watch2 ? normalizeResolvedWatchIgnorePaths(configuredWatchIgnore, resolvedCwd) : [],
|
|
1120
|
+
watchDebounce: parsed.watchDebounce ?? base?.watchDebounce ?? DEFAULT_WATCH_DEBOUNCE,
|
|
1121
|
+
healthCheck
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
function resolvePmStartDefinitions(parsed, config, workspaceRoot) {
|
|
1125
|
+
const configApps = getConfiguredPmApps(config);
|
|
1126
|
+
const selection = resolveStartSelection(configApps, parsed, workspaceRoot);
|
|
1127
|
+
if (selection.startAll) {
|
|
1128
|
+
if (configApps.length === 0) {
|
|
1129
|
+
throw new Error("No pm apps configured in elit.config.* and no start target was provided.");
|
|
1130
|
+
}
|
|
1131
|
+
return configApps.map((app) => resolvePmAppDefinition(app, { ...parsed, name: app.name }, workspaceRoot, "config"));
|
|
1132
|
+
}
|
|
1133
|
+
if (selection.selected) {
|
|
1134
|
+
return [resolvePmAppDefinition(selection.selected, parsed, workspaceRoot, "config")];
|
|
1135
|
+
}
|
|
1136
|
+
return [resolvePmAppDefinition(void 0, parsed, workspaceRoot, "cli")];
|
|
1137
|
+
}
|
|
1138
|
+
function parsePmStartArgs(args) {
|
|
1139
|
+
const parsed = {
|
|
1140
|
+
env: {},
|
|
1141
|
+
watchPaths: [],
|
|
1142
|
+
watchIgnore: []
|
|
1143
|
+
};
|
|
1144
|
+
for (let index = 0; index < args.length; index++) {
|
|
1145
|
+
const arg = args[index];
|
|
1146
|
+
switch (arg) {
|
|
1147
|
+
case "--script":
|
|
1148
|
+
parsed.script = readRequiredValue(args, ++index, "--script");
|
|
1149
|
+
break;
|
|
1150
|
+
case "--file":
|
|
1151
|
+
case "-f":
|
|
1152
|
+
parsed.file = readRequiredValue(args, ++index, arg);
|
|
1153
|
+
break;
|
|
1154
|
+
case "--wapk":
|
|
1155
|
+
parsed.wapk = readRequiredValue(args, ++index, "--wapk");
|
|
1156
|
+
break;
|
|
1157
|
+
case "--google-drive-file-id":
|
|
1158
|
+
parsed.wapkRun = {
|
|
1159
|
+
...parsed.wapkRun,
|
|
1160
|
+
googleDrive: {
|
|
1161
|
+
...parsed.wapkRun?.googleDrive,
|
|
1162
|
+
fileId: readRequiredValue(args, ++index, "--google-drive-file-id")
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
break;
|
|
1166
|
+
case "--google-drive-token-env":
|
|
1167
|
+
parsed.wapkRun = {
|
|
1168
|
+
...parsed.wapkRun,
|
|
1169
|
+
googleDrive: {
|
|
1170
|
+
...parsed.wapkRun?.googleDrive,
|
|
1171
|
+
accessTokenEnv: readRequiredValue(args, ++index, "--google-drive-token-env")
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
break;
|
|
1175
|
+
case "--google-drive-access-token":
|
|
1176
|
+
parsed.wapkRun = {
|
|
1177
|
+
...parsed.wapkRun,
|
|
1178
|
+
googleDrive: {
|
|
1179
|
+
...parsed.wapkRun?.googleDrive,
|
|
1180
|
+
accessToken: readRequiredValue(args, ++index, "--google-drive-access-token")
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
break;
|
|
1184
|
+
case "--google-drive-shared-drive":
|
|
1185
|
+
parsed.wapkRun = {
|
|
1186
|
+
...parsed.wapkRun,
|
|
1187
|
+
googleDrive: {
|
|
1188
|
+
...parsed.wapkRun?.googleDrive,
|
|
1189
|
+
supportsAllDrives: true
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
break;
|
|
1193
|
+
case "--runtime":
|
|
1194
|
+
case "-r":
|
|
1195
|
+
parsed.runtime = normalizePmRuntime(readRequiredValue(args, ++index, arg), arg);
|
|
1196
|
+
break;
|
|
1197
|
+
case "--name":
|
|
1198
|
+
case "-n":
|
|
1199
|
+
parsed.name = readRequiredValue(args, ++index, arg);
|
|
1200
|
+
break;
|
|
1201
|
+
case "--cwd":
|
|
1202
|
+
parsed.cwd = readRequiredValue(args, ++index, "--cwd");
|
|
1203
|
+
break;
|
|
1204
|
+
case "--env": {
|
|
1205
|
+
const [key, value] = parsePmEnvEntry(readRequiredValue(args, ++index, "--env"));
|
|
1206
|
+
parsed.env[key] = value;
|
|
1207
|
+
break;
|
|
1208
|
+
}
|
|
1209
|
+
case "--password":
|
|
1210
|
+
parsed.password = readRequiredValue(args, ++index, "--password");
|
|
1211
|
+
break;
|
|
1212
|
+
case "--online":
|
|
1213
|
+
parsed.wapkRun = {
|
|
1214
|
+
...parsed.wapkRun,
|
|
1215
|
+
online: true
|
|
1216
|
+
};
|
|
1217
|
+
break;
|
|
1218
|
+
case "--online-url":
|
|
1219
|
+
parsed.wapkRun = {
|
|
1220
|
+
...parsed.wapkRun,
|
|
1221
|
+
online: true,
|
|
1222
|
+
onlineUrl: readRequiredValue(args, ++index, "--online-url")
|
|
1223
|
+
};
|
|
1224
|
+
break;
|
|
1225
|
+
case "--sync-interval":
|
|
1226
|
+
parsed.wapkRun = {
|
|
1227
|
+
...parsed.wapkRun,
|
|
1228
|
+
syncInterval: normalizeIntegerOption(readRequiredValue(args, ++index, "--sync-interval"), "--sync-interval", 50)
|
|
1229
|
+
};
|
|
1230
|
+
break;
|
|
1231
|
+
case "--watcher":
|
|
1232
|
+
case "--use-watcher":
|
|
1233
|
+
parsed.wapkRun = {
|
|
1234
|
+
...parsed.wapkRun,
|
|
1235
|
+
useWatcher: true
|
|
1236
|
+
};
|
|
1237
|
+
break;
|
|
1238
|
+
case "--archive-watch":
|
|
1239
|
+
parsed.wapkRun = {
|
|
1240
|
+
...parsed.wapkRun,
|
|
1241
|
+
watchArchive: true
|
|
1242
|
+
};
|
|
1243
|
+
break;
|
|
1244
|
+
case "--no-archive-watch":
|
|
1245
|
+
parsed.wapkRun = {
|
|
1246
|
+
...parsed.wapkRun,
|
|
1247
|
+
watchArchive: false
|
|
1248
|
+
};
|
|
1249
|
+
break;
|
|
1250
|
+
case "--archive-sync-interval":
|
|
1251
|
+
parsed.wapkRun = {
|
|
1252
|
+
...parsed.wapkRun,
|
|
1253
|
+
archiveSyncInterval: normalizeIntegerOption(readRequiredValue(args, ++index, "--archive-sync-interval"), "--archive-sync-interval", 50)
|
|
1254
|
+
};
|
|
1255
|
+
break;
|
|
1256
|
+
case "--restart-policy":
|
|
1257
|
+
parsed.restartPolicy = normalizePmRestartPolicy(readRequiredValue(args, ++index, "--restart-policy"));
|
|
1258
|
+
break;
|
|
1259
|
+
case "--min-uptime":
|
|
1260
|
+
parsed.minUptime = normalizeIntegerOption(readRequiredValue(args, ++index, "--min-uptime"), "--min-uptime");
|
|
1261
|
+
break;
|
|
1262
|
+
case "--watch":
|
|
1263
|
+
parsed.watch = true;
|
|
1264
|
+
break;
|
|
1265
|
+
case "--watch-path":
|
|
1266
|
+
parsed.watch = true;
|
|
1267
|
+
parsed.watchPaths.push(readRequiredValue(args, ++index, "--watch-path"));
|
|
1268
|
+
break;
|
|
1269
|
+
case "--watch-ignore":
|
|
1270
|
+
parsed.watch = true;
|
|
1271
|
+
parsed.watchIgnore.push(readRequiredValue(args, ++index, "--watch-ignore"));
|
|
1272
|
+
break;
|
|
1273
|
+
case "--watch-debounce":
|
|
1274
|
+
parsed.watch = true;
|
|
1275
|
+
parsed.watchDebounce = normalizeIntegerOption(readRequiredValue(args, ++index, "--watch-debounce"), "--watch-debounce");
|
|
1276
|
+
break;
|
|
1277
|
+
case "--health-url":
|
|
1278
|
+
parsed.healthCheckUrl = readRequiredValue(args, ++index, "--health-url");
|
|
1279
|
+
break;
|
|
1280
|
+
case "--health-grace-period":
|
|
1281
|
+
parsed.healthCheckGracePeriod = normalizeIntegerOption(readRequiredValue(args, ++index, "--health-grace-period"), "--health-grace-period");
|
|
1282
|
+
break;
|
|
1283
|
+
case "--health-interval":
|
|
1284
|
+
parsed.healthCheckInterval = normalizeIntegerOption(readRequiredValue(args, ++index, "--health-interval"), "--health-interval", 250);
|
|
1285
|
+
break;
|
|
1286
|
+
case "--health-timeout":
|
|
1287
|
+
parsed.healthCheckTimeout = normalizeIntegerOption(readRequiredValue(args, ++index, "--health-timeout"), "--health-timeout", 250);
|
|
1288
|
+
break;
|
|
1289
|
+
case "--health-max-failures":
|
|
1290
|
+
parsed.healthCheckMaxFailures = normalizeIntegerOption(readRequiredValue(args, ++index, "--health-max-failures"), "--health-max-failures", 1);
|
|
1291
|
+
break;
|
|
1292
|
+
case "--no-autorestart":
|
|
1293
|
+
parsed.autorestart = false;
|
|
1294
|
+
break;
|
|
1295
|
+
case "--restart-delay":
|
|
1296
|
+
parsed.restartDelay = normalizeIntegerOption(readRequiredValue(args, ++index, "--restart-delay"), "--restart-delay");
|
|
1297
|
+
break;
|
|
1298
|
+
case "--max-restarts":
|
|
1299
|
+
parsed.maxRestarts = normalizeIntegerOption(readRequiredValue(args, ++index, "--max-restarts"), "--max-restarts");
|
|
1300
|
+
break;
|
|
1301
|
+
default:
|
|
1302
|
+
if (arg.startsWith("-")) {
|
|
1303
|
+
throw new Error(`Unknown pm option: ${arg}`);
|
|
1304
|
+
}
|
|
1305
|
+
if (parsed.targetToken) {
|
|
1306
|
+
throw new Error("pm start accepts at most one positional target.");
|
|
1307
|
+
}
|
|
1308
|
+
parsed.targetToken = arg;
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (countDefinedPmWapkSources(parsed.wapk, parsed.wapkRun) > 1) {
|
|
1313
|
+
throw new Error("Use only one WAPK archive source per pm start: --wapk or --google-drive-file-id.");
|
|
1314
|
+
}
|
|
1315
|
+
const explicitTargets = [parsed.script, parsed.file, resolvePmWapkSourceToken(parsed.wapk, parsed.wapkRun)].filter(Boolean);
|
|
1316
|
+
if (explicitTargets.length > 1) {
|
|
1317
|
+
throw new Error("Use only one target type per pm start: --script, --file, or --wapk.");
|
|
1318
|
+
}
|
|
1319
|
+
if (parsed.healthCheckUrl && !/^https?:\/\//i.test(parsed.healthCheckUrl)) {
|
|
1320
|
+
throw new Error("--health-url must be an absolute http:// or https:// URL");
|
|
1321
|
+
}
|
|
1322
|
+
return parsed;
|
|
1323
|
+
}
|
|
1324
|
+
function printPmHelp() {
|
|
1325
|
+
console.log([
|
|
1326
|
+
"",
|
|
1327
|
+
"Elit PM - lightweight process manager",
|
|
1328
|
+
"",
|
|
1329
|
+
"Usage:",
|
|
1330
|
+
' elit pm start --script "npm start" --name my-app --runtime node',
|
|
1331
|
+
" elit pm start --wapk ./test.wapk --name my-app",
|
|
1332
|
+
" elit pm start --wapk gdrive://<fileId> --name my-app",
|
|
1333
|
+
" elit pm start --google-drive-file-id <fileId> --name my-app",
|
|
1334
|
+
" elit pm start ./app.ts --name my-app",
|
|
1335
|
+
" elit pm start --file ./app.js --name my-app",
|
|
1336
|
+
" elit pm start my-app",
|
|
1337
|
+
" elit pm start",
|
|
1338
|
+
" elit pm list",
|
|
1339
|
+
" elit pm stop <name|all>",
|
|
1340
|
+
" elit pm restart <name|all>",
|
|
1341
|
+
" elit pm delete <name|all>",
|
|
1342
|
+
" elit pm save",
|
|
1343
|
+
" elit pm resurrect",
|
|
1344
|
+
" elit pm logs <name> --lines 100",
|
|
1345
|
+
"",
|
|
1346
|
+
"Start Options:",
|
|
1347
|
+
" --script <command> Run a shell command, for example: npm start",
|
|
1348
|
+
" --file, -f <path> Run a .js/.mjs/.cjs/.ts file",
|
|
1349
|
+
" --wapk <source> Run a local .wapk file or a remote source like gdrive://<fileId>",
|
|
1350
|
+
" --google-drive-file-id <id> Run a WAPK archive directly from Google Drive",
|
|
1351
|
+
" --google-drive-token-env <name> Env var containing the Google Drive OAuth token",
|
|
1352
|
+
" --google-drive-access-token <value> OAuth token forwarded to elit wapk run",
|
|
1353
|
+
" --google-drive-shared-drive Forward supportsAllDrives=true for shared drives",
|
|
1354
|
+
" --runtime, -r <name> Runtime override: node, bun, deno",
|
|
1355
|
+
" --name, -n <name> Process name used by list/stop/restart",
|
|
1356
|
+
" --cwd <dir> Working directory for the managed process",
|
|
1357
|
+
" --env KEY=VALUE Add or override an environment variable",
|
|
1358
|
+
" --password <value> Password for locked WAPK archives",
|
|
1359
|
+
" --online Host the WAPK on Elit Run through PM instead of a local runtime",
|
|
1360
|
+
" --online-url <url> Elit Run URL used for PM-managed online WAPK hosting",
|
|
1361
|
+
" --sync-interval <ms> Forward WAPK live-sync write interval (>= 50ms)",
|
|
1362
|
+
" --watcher, --use-watcher Forward event-driven WAPK file watching",
|
|
1363
|
+
" --archive-watch Pull archive source changes back into the temp WAPK workdir",
|
|
1364
|
+
" --no-archive-watch Disable archive-source read sync for WAPK apps",
|
|
1365
|
+
" --archive-sync-interval <ms> Forward WAPK archive read-sync interval (>= 50ms)",
|
|
1366
|
+
" --restart-policy <mode> Restart policy: always, on-failure, never",
|
|
1367
|
+
" --min-uptime <ms> Reset crash counter after this healthy uptime",
|
|
1368
|
+
" --watch Restart when watched files change",
|
|
1369
|
+
" --watch-path <path> Add a file or directory to watch",
|
|
1370
|
+
" --watch-ignore <pattern> Ignore watched paths matching this glob-like pattern",
|
|
1371
|
+
" --watch-debounce <ms> Debounce file-triggered restarts (default 250)",
|
|
1372
|
+
" --health-url <url> Poll an HTTP endpoint and restart after repeated failures",
|
|
1373
|
+
" --health-grace-period <ms> Delay before the first health check (default 5000)",
|
|
1374
|
+
" --health-interval <ms> Health check interval (default 10000)",
|
|
1375
|
+
" --health-timeout <ms> Per-request health check timeout (default 3000)",
|
|
1376
|
+
" --health-max-failures <n> Consecutive failures before restart (default 3)",
|
|
1377
|
+
" --no-autorestart Disable automatic restart",
|
|
1378
|
+
" --restart-delay <ms> Delay between restart attempts (default 1000)",
|
|
1379
|
+
" --max-restarts <count> Maximum restart attempts (default 10)",
|
|
1380
|
+
"",
|
|
1381
|
+
"Config:",
|
|
1382
|
+
" Add pm.apps[] to elit.config.* and run elit pm start to boot all configured apps.",
|
|
1383
|
+
"",
|
|
1384
|
+
"Example:",
|
|
1385
|
+
" export default {",
|
|
1386
|
+
" pm: {",
|
|
1387
|
+
" apps: [",
|
|
1388
|
+
' { name: "api", script: "npm start", cwd: ".", runtime: "node" },',
|
|
1389
|
+
' { name: "worker", file: "./src/worker.ts", runtime: "bun" },',
|
|
1390
|
+
' { name: "desktop-app", wapk: "./dist/app.wapk", runtime: "node" },',
|
|
1391
|
+
' { name: "drive-app", wapkRun: { googleDrive: { fileId: "1AbCdEfGhIjKlMnOp", accessTokenEnv: "GOOGLE_DRIVE_ACCESS_TOKEN" }, useWatcher: true, watchArchive: true } }',
|
|
1392
|
+
' { name: "online-app", wapk: "./dist/app.wapk", wapkRun: { online: true, onlineUrl: "http://localhost:4179" } }',
|
|
1393
|
+
" ]",
|
|
1394
|
+
" }",
|
|
1395
|
+
" }",
|
|
1396
|
+
"",
|
|
1397
|
+
"Notes:",
|
|
1398
|
+
" - PM state and logs are stored in ./.elit/pm by default.",
|
|
1399
|
+
" - elit pm save persists running apps to pm.dumpFile or ./.elit/pm/dump.json.",
|
|
1400
|
+
" - elit pm resurrect restarts whatever was last saved by elit pm save.",
|
|
1401
|
+
" - elit pm start <name> starts a configured app by name.",
|
|
1402
|
+
" - TypeScript files with runtime node require tsx, otherwise use --runtime bun.",
|
|
1403
|
+
" - WAPK processes are executed through elit wapk run inside the manager.",
|
|
1404
|
+
" - WAPK PM apps can use local archives, gdrive://<fileId>, or pm.apps[].wapkRun.googleDrive.",
|
|
1405
|
+
" - PM-managed WAPK online hosts close their Elit Run share session when you use elit pm stop, restart, or delete."
|
|
1406
|
+
].join("\n"));
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
// src/cli/pm/process.ts
|
|
1410
|
+
import { spawn, spawnSync } from "child_process";
|
|
1411
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1412
|
+
import { basename, join as join4 } from "path";
|
|
1413
|
+
function readCurrentCliInvocation() {
|
|
1414
|
+
const cliEntry = process.argv[1];
|
|
1415
|
+
if (!cliEntry) {
|
|
1416
|
+
throw new Error("Unable to resolve the current Elit CLI entrypoint for pm runner startup.");
|
|
1417
|
+
}
|
|
1418
|
+
return {
|
|
1419
|
+
command: process.execPath,
|
|
1420
|
+
args: [...process.execArgv, cliEntry]
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
function preferCurrentExecutable(runtime2) {
|
|
1424
|
+
const executableName = basename(process.execPath).toLowerCase();
|
|
1425
|
+
if (runtime2 === "node" && process.release?.name === "node" && executableName.startsWith("node")) {
|
|
1426
|
+
return process.execPath;
|
|
1427
|
+
}
|
|
1428
|
+
if (runtime2 === "bun" && process.versions?.bun && executableName.startsWith("bun")) {
|
|
1429
|
+
return process.execPath;
|
|
1430
|
+
}
|
|
1431
|
+
return runtime2;
|
|
1432
|
+
}
|
|
1433
|
+
function commandExists(command) {
|
|
1434
|
+
if (command.includes("\\") || command.includes("/")) {
|
|
1435
|
+
return existsSync3(command);
|
|
1436
|
+
}
|
|
1437
|
+
const result = spawnSync(command, ["--version"], {
|
|
1438
|
+
stdio: "ignore",
|
|
1439
|
+
windowsHide: true
|
|
1440
|
+
});
|
|
1441
|
+
return !result.error;
|
|
1442
|
+
}
|
|
1443
|
+
function ensureCommandAvailable(command, displayName) {
|
|
1444
|
+
if (commandExists(command)) {
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
throw new Error(`${displayName} was not found in PATH.`);
|
|
1448
|
+
}
|
|
1449
|
+
function resolveTsxExecutable(cwd) {
|
|
1450
|
+
const localPath = join4(cwd, "node_modules", ".bin", process.platform === "win32" ? "tsx.cmd" : "tsx");
|
|
1451
|
+
if (existsSync3(localPath)) {
|
|
1452
|
+
return localPath;
|
|
1453
|
+
}
|
|
1454
|
+
const globalCommand = process.platform === "win32" ? "tsx.cmd" : "tsx";
|
|
1455
|
+
return commandExists(globalCommand) ? globalCommand : void 0;
|
|
1456
|
+
}
|
|
1457
|
+
function inferRuntimeFromFile(filePath) {
|
|
1458
|
+
if (isTypescriptFile(filePath) && commandExists("bun")) {
|
|
1459
|
+
return "bun";
|
|
1460
|
+
}
|
|
1461
|
+
return "node";
|
|
1462
|
+
}
|
|
1463
|
+
function isPmOnlineWapkRecord(record) {
|
|
1464
|
+
return record.type === "wapk" && isPmWapkOnlineRunConfig(record.wapkRun);
|
|
1465
|
+
}
|
|
1466
|
+
function buildPmCommand(record) {
|
|
1467
|
+
if (record.type === "script") {
|
|
1468
|
+
return {
|
|
1469
|
+
command: record.script,
|
|
1470
|
+
args: [],
|
|
1471
|
+
shell: true,
|
|
1472
|
+
runtime: record.runtime,
|
|
1473
|
+
preview: record.script
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
if (record.type === "wapk") {
|
|
1477
|
+
const cliInvocation = readCurrentCliInvocation();
|
|
1478
|
+
const args = [
|
|
1479
|
+
...cliInvocation.args,
|
|
1480
|
+
"wapk",
|
|
1481
|
+
"run",
|
|
1482
|
+
record.wapk
|
|
1483
|
+
];
|
|
1484
|
+
const previewParts = ["elit", "wapk", "run", quoteCommandSegment(record.wapk)];
|
|
1485
|
+
const online = isPmOnlineWapkRecord(record);
|
|
1486
|
+
if (record.runtime && !online) {
|
|
1487
|
+
args.push("--runtime", record.runtime);
|
|
1488
|
+
previewParts.push("--runtime", record.runtime);
|
|
1489
|
+
}
|
|
1490
|
+
if (record.password) {
|
|
1491
|
+
args.push("--password", record.password);
|
|
1492
|
+
previewParts.push("--password", "******");
|
|
1493
|
+
}
|
|
1494
|
+
appendPmWapkRunArgs(args, previewParts, record.wapkRun);
|
|
1495
|
+
return {
|
|
1496
|
+
command: cliInvocation.command,
|
|
1497
|
+
args,
|
|
1498
|
+
env: online ? { [PM_WAPK_ONLINE_STDIN_SHUTDOWN_ENV]: "1" } : void 0,
|
|
1499
|
+
preview: previewParts.join(" "),
|
|
1500
|
+
runtime: online ? void 0 : record.runtime
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
const runtime2 = record.runtime ?? inferRuntimeFromFile(record.file);
|
|
1504
|
+
if (runtime2 === "bun") {
|
|
1505
|
+
const executable2 = preferCurrentExecutable("bun");
|
|
1506
|
+
ensureCommandAvailable(executable2, "Bun runtime");
|
|
1507
|
+
return {
|
|
1508
|
+
command: executable2,
|
|
1509
|
+
args: ["run", record.file],
|
|
1510
|
+
runtime: runtime2,
|
|
1511
|
+
preview: `${basename(executable2)} run ${quoteCommandSegment(record.file)}`
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
if (runtime2 === "deno") {
|
|
1515
|
+
const executable2 = preferCurrentExecutable("deno");
|
|
1516
|
+
ensureCommandAvailable(executable2, "Deno runtime");
|
|
1517
|
+
return {
|
|
1518
|
+
command: executable2,
|
|
1519
|
+
args: ["run", "--allow-all", record.file],
|
|
1520
|
+
runtime: runtime2,
|
|
1521
|
+
preview: `${basename(executable2)} run --allow-all ${quoteCommandSegment(record.file)}`
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
if (isTypescriptFile(record.file)) {
|
|
1525
|
+
const tsxExecutable = resolveTsxExecutable(record.cwd);
|
|
1526
|
+
if (!tsxExecutable) {
|
|
1527
|
+
throw new Error('TypeScript file execution with runtime "node" requires tsx to be installed, or use --runtime bun.');
|
|
1528
|
+
}
|
|
1529
|
+
return {
|
|
1530
|
+
command: tsxExecutable,
|
|
1531
|
+
args: [record.file],
|
|
1532
|
+
runtime: runtime2,
|
|
1533
|
+
preview: `${basename(tsxExecutable)} ${quoteCommandSegment(record.file)}`
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
const executable = preferCurrentExecutable("node");
|
|
1537
|
+
ensureCommandAvailable(executable, "Node.js runtime");
|
|
1538
|
+
return {
|
|
1539
|
+
command: executable,
|
|
1540
|
+
args: [record.file],
|
|
1541
|
+
runtime: runtime2,
|
|
1542
|
+
preview: `${basename(executable)} ${quoteCommandSegment(record.file)}`
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
function createRecordFromDefinition(definition, paths, existing) {
|
|
1546
|
+
const id = sanitizePmProcessName(definition.name);
|
|
1547
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1548
|
+
const preview = definition.type === "script" ? definition.script : definition.type === "wapk" ? buildPmWapkPreview(definition.wapk, definition.runtime, definition.password, definition.wapkRun) : `${definition.runtime ?? "auto"} ${quoteCommandSegment(definition.file)}`;
|
|
1549
|
+
return {
|
|
1550
|
+
id,
|
|
1551
|
+
name: definition.name,
|
|
1552
|
+
type: definition.type,
|
|
1553
|
+
source: definition.source,
|
|
1554
|
+
cwd: definition.cwd,
|
|
1555
|
+
runtime: definition.runtime,
|
|
1556
|
+
env: definition.env,
|
|
1557
|
+
script: definition.script,
|
|
1558
|
+
file: definition.file,
|
|
1559
|
+
wapk: definition.wapk,
|
|
1560
|
+
wapkRun: definition.wapkRun,
|
|
1561
|
+
autorestart: definition.autorestart,
|
|
1562
|
+
restartDelay: definition.restartDelay,
|
|
1563
|
+
maxRestarts: definition.maxRestarts,
|
|
1564
|
+
password: definition.password,
|
|
1565
|
+
restartPolicy: definition.restartPolicy,
|
|
1566
|
+
minUptime: definition.minUptime,
|
|
1567
|
+
watch: definition.watch,
|
|
1568
|
+
watchPaths: definition.watchPaths,
|
|
1569
|
+
watchIgnore: definition.watchIgnore,
|
|
1570
|
+
watchDebounce: definition.watchDebounce,
|
|
1571
|
+
healthCheck: definition.healthCheck,
|
|
1572
|
+
desiredState: "running",
|
|
1573
|
+
status: "starting",
|
|
1574
|
+
commandPreview: preview,
|
|
1575
|
+
createdAt: existing?.createdAt ?? now,
|
|
1576
|
+
updatedAt: now,
|
|
1577
|
+
startedAt: void 0,
|
|
1578
|
+
stoppedAt: void 0,
|
|
1579
|
+
runnerPid: void 0,
|
|
1580
|
+
childPid: void 0,
|
|
1581
|
+
restartCount: existing?.restartCount ?? 0,
|
|
1582
|
+
lastExitCode: existing?.lastExitCode,
|
|
1583
|
+
error: void 0,
|
|
1584
|
+
logFiles: existing?.logFiles ?? {
|
|
1585
|
+
out: join4(paths.logsDir, `${id}.out.log`),
|
|
1586
|
+
err: join4(paths.logsDir, `${id}.err.log`)
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function terminateProcessTree(pid) {
|
|
1591
|
+
if (process.platform === "win32") {
|
|
1592
|
+
const result = spawnSync("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
1593
|
+
stdio: "ignore",
|
|
1594
|
+
windowsHide: true
|
|
1595
|
+
});
|
|
1596
|
+
if (result.error && result.error.code !== "ENOENT") {
|
|
1597
|
+
throw result.error;
|
|
1598
|
+
}
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
try {
|
|
1602
|
+
process.kill(pid, "SIGTERM");
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
const code = error.code;
|
|
1605
|
+
if (code !== "ESRCH") {
|
|
1606
|
+
throw error;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
async function startManagedProcess(definition, paths) {
|
|
1611
|
+
ensurePmDirectories(paths);
|
|
1612
|
+
const id = sanitizePmProcessName(definition.name);
|
|
1613
|
+
const recordPath = getPmRecordPath(paths, id);
|
|
1614
|
+
const existingMatch = existsSync3(recordPath) ? syncPmRecordLiveness({ filePath: recordPath, record: readPmRecord(recordPath) }) : void 0;
|
|
1615
|
+
if (existingMatch?.record.runnerPid && isProcessAlive(existingMatch.record.runnerPid)) {
|
|
1616
|
+
throw new Error(`Process "${definition.name}" is already running.`);
|
|
1617
|
+
}
|
|
1618
|
+
const record = createRecordFromDefinition(definition, paths, existingMatch?.record);
|
|
1619
|
+
writePmRecord(recordPath, record);
|
|
1620
|
+
const cliInvocation = readCurrentCliInvocation();
|
|
1621
|
+
const runner = spawn(cliInvocation.command, [
|
|
1622
|
+
...cliInvocation.args,
|
|
1623
|
+
"pm",
|
|
1624
|
+
"__run",
|
|
1625
|
+
"--data-dir",
|
|
1626
|
+
paths.dataDir,
|
|
1627
|
+
"--id",
|
|
1628
|
+
record.id
|
|
1629
|
+
], {
|
|
1630
|
+
cwd: definition.cwd,
|
|
1631
|
+
detached: true,
|
|
1632
|
+
stdio: "ignore",
|
|
1633
|
+
windowsHide: true,
|
|
1634
|
+
env: {
|
|
1635
|
+
...process.env,
|
|
1636
|
+
ELIT_PM_INTERNAL: "1"
|
|
1637
|
+
}
|
|
1638
|
+
});
|
|
1639
|
+
if (!runner.pid) {
|
|
1640
|
+
throw new Error(`Failed to start process runner for "${definition.name}".`);
|
|
1641
|
+
}
|
|
1642
|
+
runner.unref();
|
|
1643
|
+
const startedRecord = {
|
|
1644
|
+
...record,
|
|
1645
|
+
runnerPid: runner.pid,
|
|
1646
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1647
|
+
};
|
|
1648
|
+
writePmRecord(recordPath, startedRecord);
|
|
1649
|
+
return startedRecord;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// src/cli/pm/runner.ts
|
|
1653
|
+
import { spawn as spawn2 } from "child_process";
|
|
1654
|
+
import { createWriteStream, mkdirSync as mkdirSync3 } from "fs";
|
|
1655
|
+
import { EOL } from "os";
|
|
1656
|
+
import { dirname as dirname3, join as join6, resolve as resolve5 } from "path";
|
|
1657
|
+
|
|
1658
|
+
// src/server/chokidar/watch.ts
|
|
1659
|
+
init_runtime();
|
|
1660
|
+
|
|
1661
|
+
// src/server/chokidar/utils.ts
|
|
1662
|
+
init_fs();
|
|
1663
|
+
|
|
1664
|
+
// src/server/path/platform.ts
|
|
1665
|
+
init_runtime();
|
|
1666
|
+
function getSeparator(isWin) {
|
|
1667
|
+
return isWin ? "\\" : "/";
|
|
1668
|
+
}
|
|
1669
|
+
function getCwd() {
|
|
1670
|
+
if (isNode || isBun) {
|
|
1671
|
+
return process.cwd();
|
|
1672
|
+
}
|
|
1673
|
+
if (isDeno) {
|
|
1674
|
+
return Deno.cwd();
|
|
1675
|
+
}
|
|
1676
|
+
return "/";
|
|
1677
|
+
}
|
|
1678
|
+
var isWindows = (() => {
|
|
1679
|
+
if (isNode) {
|
|
1680
|
+
return process.platform === "win32";
|
|
1681
|
+
}
|
|
1682
|
+
if (isDeno) {
|
|
1683
|
+
return Deno.build.os === "windows";
|
|
1684
|
+
}
|
|
1685
|
+
return typeof process !== "undefined" && process.platform === "win32";
|
|
1686
|
+
})();
|
|
1687
|
+
|
|
1688
|
+
// src/server/path/operations.ts
|
|
1689
|
+
function findLastSeparator(path) {
|
|
1690
|
+
return Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
1691
|
+
}
|
|
1692
|
+
function isAbsolutePosix(path) {
|
|
1693
|
+
return path.length > 0 && path[0] === "/";
|
|
1694
|
+
}
|
|
1695
|
+
function isAbsoluteWin(path) {
|
|
1696
|
+
const len = path.length;
|
|
1697
|
+
if (len === 0) return false;
|
|
1698
|
+
const code = path.charCodeAt(0);
|
|
1699
|
+
if (code === 47 || code === 92) {
|
|
1700
|
+
return true;
|
|
1701
|
+
}
|
|
1702
|
+
if (code >= 65 && code <= 90 || code >= 97 && code <= 122) {
|
|
1703
|
+
if (len > 2 && path.charCodeAt(1) === 58) {
|
|
1704
|
+
const code2 = path.charCodeAt(2);
|
|
1705
|
+
if (code2 === 47 || code2 === 92) {
|
|
1706
|
+
return true;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
return false;
|
|
1711
|
+
}
|
|
1712
|
+
function normalizePath(path, isWin) {
|
|
1713
|
+
if (path.length === 0) return ".";
|
|
1714
|
+
const separator = getSeparator(isWin);
|
|
1715
|
+
const isAbsolute = isWin ? isAbsoluteWin(path) : isAbsolutePosix(path);
|
|
1716
|
+
const trailingSeparator = path[path.length - 1] === separator || isWin && path[path.length - 1] === "/";
|
|
1717
|
+
const normalized = path.replace(isWin ? /[\/\\]+/g : /\/+/g, separator);
|
|
1718
|
+
const parts = normalized.split(separator);
|
|
1719
|
+
const result = [];
|
|
1720
|
+
for (let index = 0; index < parts.length; index++) {
|
|
1721
|
+
const part = parts[index];
|
|
1722
|
+
if (part === "" || part === ".") {
|
|
1723
|
+
if (index === 0 && isAbsolute) result.push("");
|
|
1724
|
+
continue;
|
|
1725
|
+
}
|
|
1726
|
+
if (part === "..") {
|
|
1727
|
+
if (result.length > 0 && result[result.length - 1] !== "..") {
|
|
1728
|
+
if (!(result.length === 1 && result[0] === "")) {
|
|
1729
|
+
result.pop();
|
|
1730
|
+
}
|
|
1731
|
+
} else if (!isAbsolute) {
|
|
1732
|
+
result.push("..");
|
|
1733
|
+
}
|
|
1734
|
+
} else {
|
|
1735
|
+
result.push(part);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
let finalPath = result.join(separator);
|
|
1739
|
+
if (finalPath.length === 0) {
|
|
1740
|
+
return isAbsolute ? separator : ".";
|
|
1741
|
+
}
|
|
1742
|
+
if (trailingSeparator && finalPath[finalPath.length - 1] !== separator) {
|
|
1743
|
+
finalPath += separator;
|
|
1744
|
+
}
|
|
1745
|
+
return finalPath;
|
|
1746
|
+
}
|
|
1747
|
+
function joinPaths(paths, isWin) {
|
|
1748
|
+
if (paths.length === 0) return ".";
|
|
1749
|
+
const separator = getSeparator(isWin);
|
|
1750
|
+
let joined = "";
|
|
1751
|
+
for (let index = 0; index < paths.length; index++) {
|
|
1752
|
+
const path = paths[index];
|
|
1753
|
+
if (path && path.length > 0) {
|
|
1754
|
+
if (joined.length === 0) {
|
|
1755
|
+
joined = path;
|
|
1756
|
+
} else {
|
|
1757
|
+
joined += separator + path;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
if (joined.length === 0) return ".";
|
|
1762
|
+
return normalizePath(joined, isWin);
|
|
1763
|
+
}
|
|
1764
|
+
function resolvePaths(paths, isWin) {
|
|
1765
|
+
const separator = getSeparator(isWin);
|
|
1766
|
+
let resolved = "";
|
|
1767
|
+
let absolute = false;
|
|
1768
|
+
for (let index = paths.length - 1; index >= 0 && !absolute; index--) {
|
|
1769
|
+
const path = paths[index];
|
|
1770
|
+
if (path && path.length > 0) {
|
|
1771
|
+
resolved = path + (resolved.length > 0 ? separator + resolved : "");
|
|
1772
|
+
absolute = isWin ? isAbsoluteWin(resolved) : isAbsolutePosix(resolved);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
if (!absolute) {
|
|
1776
|
+
const cwd = getCwd();
|
|
1777
|
+
resolved = cwd + (resolved.length > 0 ? separator + resolved : "");
|
|
1778
|
+
}
|
|
1779
|
+
return normalizePath(resolved, isWin);
|
|
1780
|
+
}
|
|
1781
|
+
function relativePath(from, to, isWin) {
|
|
1782
|
+
const resolvedFrom = resolvePaths([from], isWin);
|
|
1783
|
+
const resolvedTo = resolvePaths([to], isWin);
|
|
1784
|
+
if (resolvedFrom === resolvedTo) return "";
|
|
1785
|
+
const separator = getSeparator(isWin);
|
|
1786
|
+
const fromParts = resolvedFrom.split(separator).filter((part) => part.length > 0);
|
|
1787
|
+
const toParts = resolvedTo.split(separator).filter((part) => part.length > 0);
|
|
1788
|
+
const minLength = Math.min(fromParts.length, toParts.length);
|
|
1789
|
+
let commonLength = 0;
|
|
1790
|
+
for (let index = 0; index < minLength; index++) {
|
|
1791
|
+
if (fromParts[index] === toParts[index]) {
|
|
1792
|
+
commonLength++;
|
|
1793
|
+
} else {
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
const result = [];
|
|
1798
|
+
const upCount = fromParts.length - commonLength;
|
|
1799
|
+
for (let index = 0; index < upCount; index++) {
|
|
1800
|
+
result.push("..");
|
|
1801
|
+
}
|
|
1802
|
+
for (let index = commonLength; index < toParts.length; index++) {
|
|
1803
|
+
result.push(toParts[index]);
|
|
1804
|
+
}
|
|
1805
|
+
return result.join(separator) || ".";
|
|
1806
|
+
}
|
|
1807
|
+
function getDirname(path, isWin) {
|
|
1808
|
+
if (path.length === 0) return ".";
|
|
1809
|
+
const separator = getSeparator(isWin);
|
|
1810
|
+
const normalized = normalizePath(path, isWin);
|
|
1811
|
+
const lastSeparatorIndex = normalized.lastIndexOf(separator);
|
|
1812
|
+
if (lastSeparatorIndex === -1) return ".";
|
|
1813
|
+
if (lastSeparatorIndex === 0) return separator;
|
|
1814
|
+
return normalized.slice(0, lastSeparatorIndex);
|
|
1815
|
+
}
|
|
1816
|
+
function getBasename(path, ext, isWin) {
|
|
1817
|
+
if (path.length === 0) return "";
|
|
1818
|
+
const lastSeparatorIndex = isWin ? findLastSeparator(path) : path.lastIndexOf("/");
|
|
1819
|
+
let base = lastSeparatorIndex === -1 ? path : path.slice(lastSeparatorIndex + 1);
|
|
1820
|
+
if (ext && base.endsWith(ext)) {
|
|
1821
|
+
base = base.slice(0, base.length - ext.length);
|
|
1822
|
+
}
|
|
1823
|
+
return base;
|
|
1824
|
+
}
|
|
1825
|
+
function getExtname(path) {
|
|
1826
|
+
const lastDotIndex = path.lastIndexOf(".");
|
|
1827
|
+
const lastSeparatorIndex = findLastSeparator(path);
|
|
1828
|
+
if (lastDotIndex === -1 || lastDotIndex < lastSeparatorIndex || lastDotIndex === path.length - 1) {
|
|
1829
|
+
return "";
|
|
1830
|
+
}
|
|
1831
|
+
return path.slice(lastDotIndex);
|
|
1832
|
+
}
|
|
1833
|
+
function parsePath(path, isWin) {
|
|
1834
|
+
let root = "";
|
|
1835
|
+
if (isWin) {
|
|
1836
|
+
if (path.length >= 2 && path[1] === ":") {
|
|
1837
|
+
root = path.slice(0, 2);
|
|
1838
|
+
if (path.length > 2 && (path[2] === "\\" || path[2] === "/")) {
|
|
1839
|
+
root += "\\";
|
|
1840
|
+
}
|
|
1841
|
+
} else if (path[0] === "\\" || path[0] === "/") {
|
|
1842
|
+
root = "\\";
|
|
1843
|
+
}
|
|
1844
|
+
} else if (path[0] === "/") {
|
|
1845
|
+
root = "/";
|
|
1846
|
+
}
|
|
1847
|
+
const dir = getDirname(path, isWin);
|
|
1848
|
+
const base = getBasename(path, void 0, isWin);
|
|
1849
|
+
const ext = getExtname(path);
|
|
1850
|
+
const name = ext ? base.slice(0, base.length - ext.length) : base;
|
|
1851
|
+
return { root, dir, base, ext, name };
|
|
1852
|
+
}
|
|
1853
|
+
function formatPath(pathObject, isWin) {
|
|
1854
|
+
const separator = getSeparator(isWin);
|
|
1855
|
+
const dir = pathObject.dir || pathObject.root || "";
|
|
1856
|
+
const base = pathObject.base || (pathObject.name || "") + (pathObject.ext || "");
|
|
1857
|
+
if (!dir) return base;
|
|
1858
|
+
if (dir === pathObject.root) return dir + base;
|
|
1859
|
+
return dir + separator + base;
|
|
1860
|
+
}
|
|
1861
|
+
function createPathOps(isWin) {
|
|
1862
|
+
return {
|
|
1863
|
+
sep: getSeparator(isWin),
|
|
1864
|
+
delimiter: isWin ? ";" : ":",
|
|
1865
|
+
normalize: (path) => normalizePath(path, isWin),
|
|
1866
|
+
join: (...paths) => joinPaths(paths, isWin),
|
|
1867
|
+
resolve: (...paths) => resolvePaths(paths, isWin),
|
|
1868
|
+
isAbsolute: (path) => isWin ? isAbsoluteWin(path) : isAbsolutePosix(path),
|
|
1869
|
+
relative: (from, to) => relativePath(from, to, isWin),
|
|
1870
|
+
dirname: (path) => getDirname(path, isWin),
|
|
1871
|
+
basename: (path, ext) => getBasename(path, ext, isWin),
|
|
1872
|
+
extname: (path) => getExtname(path),
|
|
1873
|
+
parse: (path) => parsePath(path, isWin),
|
|
1874
|
+
format: (pathObject) => formatPath(pathObject, isWin)
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/server/path/runtime.ts
|
|
1879
|
+
init_runtime();
|
|
1880
|
+
|
|
1881
|
+
// src/server/path/index.ts
|
|
1882
|
+
var posix = createPathOps(false);
|
|
1883
|
+
var win32 = createPathOps(true);
|
|
1884
|
+
function join5(...paths) {
|
|
1885
|
+
return joinPaths(paths, isWindows);
|
|
1886
|
+
}
|
|
1887
|
+
function resolve4(...paths) {
|
|
1888
|
+
return resolvePaths(paths, isWindows);
|
|
1889
|
+
}
|
|
1890
|
+
function relative(from, to) {
|
|
1891
|
+
return relativePath(from, to, isWindows);
|
|
1892
|
+
}
|
|
1893
|
+
function dirname2(path) {
|
|
1894
|
+
return getDirname(path, isWindows);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// src/server/chokidar/utils.ts
|
|
1898
|
+
function normalizePath2(path) {
|
|
1899
|
+
return path.replace(/\\/g, "/");
|
|
1900
|
+
}
|
|
1901
|
+
function emitEvent(watcher, eventType, path) {
|
|
1902
|
+
watcher.emit(eventType, path);
|
|
1903
|
+
watcher.emit("all", eventType, path);
|
|
1904
|
+
}
|
|
1905
|
+
function matchesPattern(filePath, pattern) {
|
|
1906
|
+
const regexPattern = normalizePath2(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*").replace(/\?/g, ".");
|
|
1907
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
1908
|
+
const normalizedPath = normalizePath2(filePath);
|
|
1909
|
+
return regex.test(normalizedPath);
|
|
1910
|
+
}
|
|
1911
|
+
function matchesAnyPattern(path, patterns) {
|
|
1912
|
+
return patterns.some((pattern) => matchesPattern(path, pattern));
|
|
1913
|
+
}
|
|
1914
|
+
function getBaseDirectory(pattern) {
|
|
1915
|
+
const normalizedPattern = normalizePath2(pattern);
|
|
1916
|
+
const parts = normalizedPattern.split(/[\\/]/);
|
|
1917
|
+
let baseDir = "";
|
|
1918
|
+
let sawGlob = false;
|
|
1919
|
+
for (const part of parts) {
|
|
1920
|
+
if (part.includes("*") || part.includes("?")) {
|
|
1921
|
+
sawGlob = true;
|
|
1922
|
+
break;
|
|
1923
|
+
}
|
|
1924
|
+
baseDir = baseDir ? `${baseDir}/${part}` : part;
|
|
1925
|
+
}
|
|
1926
|
+
if (sawGlob) {
|
|
1927
|
+
return baseDir || ".";
|
|
1928
|
+
}
|
|
1929
|
+
if (normalizedPattern && existsSync4(normalizedPattern)) {
|
|
1930
|
+
try {
|
|
1931
|
+
return statSync2(normalizedPattern).isDirectory() ? normalizedPattern : normalizePath2(dirname2(normalizedPattern)) || ".";
|
|
1932
|
+
} catch {
|
|
1933
|
+
return normalizePath2(dirname2(normalizedPattern)) || ".";
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
const lastSegment = parts[parts.length - 1] || "";
|
|
1937
|
+
if (lastSegment.includes(".") && !lastSegment.startsWith(".")) {
|
|
1938
|
+
return normalizePath2(dirname2(normalizedPattern)) || ".";
|
|
1939
|
+
}
|
|
1940
|
+
return normalizedPattern || ".";
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
// src/server/chokidar/native-watch.ts
|
|
1944
|
+
function handleRenameEvent(watcher, fullPath, fs2) {
|
|
1945
|
+
try {
|
|
1946
|
+
fs2.statSync(fullPath);
|
|
1947
|
+
emitEvent(watcher, "add", fullPath);
|
|
1948
|
+
} catch {
|
|
1949
|
+
emitEvent(watcher, "unlink", fullPath);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
function setupFsWatch(watcher, baseDir, patterns, fs2) {
|
|
1953
|
+
try {
|
|
1954
|
+
const nativeWatcher = fs2.watch(baseDir, { recursive: true }, (eventType, filename) => {
|
|
1955
|
+
if (!filename) return;
|
|
1956
|
+
const fullPath = normalizePath2(`${baseDir}/${filename}`);
|
|
1957
|
+
if (!matchesAnyPattern(fullPath, patterns)) return;
|
|
1958
|
+
if (eventType === "rename") {
|
|
1959
|
+
handleRenameEvent(watcher, fullPath, fs2);
|
|
1960
|
+
} else if (eventType === "change") {
|
|
1961
|
+
emitEvent(watcher, "change", fullPath);
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1964
|
+
watcher._setWatcher(nativeWatcher);
|
|
1965
|
+
watcher._trackWatchedPath(baseDir);
|
|
1966
|
+
queueMicrotask(() => watcher.emit("ready"));
|
|
1967
|
+
} catch (error) {
|
|
1968
|
+
watcher.emit("error", error);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
// src/server/chokidar/watcher.ts
|
|
1973
|
+
init_runtime();
|
|
1974
|
+
import { EventEmitter } from "events";
|
|
1975
|
+
var FSWatcher = class extends EventEmitter {
|
|
1976
|
+
constructor(options) {
|
|
1977
|
+
super();
|
|
1978
|
+
this._closed = false;
|
|
1979
|
+
this._watched = /* @__PURE__ */ new Set();
|
|
1980
|
+
this.options = options || {};
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Add paths to be watched
|
|
1984
|
+
*/
|
|
1985
|
+
add(paths) {
|
|
1986
|
+
if (this._closed) {
|
|
1987
|
+
throw new Error("Watcher has been closed");
|
|
1988
|
+
}
|
|
1989
|
+
const pathArray = Array.isArray(paths) ? paths : [paths];
|
|
1990
|
+
if (this._watcher && typeof this._watcher.add === "function") {
|
|
1991
|
+
this._watcher.add(pathArray);
|
|
1992
|
+
}
|
|
1993
|
+
if (runtime !== "node" || !this._watcher || typeof this._watcher.add !== "function") {
|
|
1994
|
+
pathArray.forEach((path) => this._trackWatchedPath(path));
|
|
1995
|
+
}
|
|
1996
|
+
return this;
|
|
1997
|
+
}
|
|
1998
|
+
/**
|
|
1999
|
+
* Stop watching paths
|
|
2000
|
+
*/
|
|
2001
|
+
unwatch(paths) {
|
|
2002
|
+
if (this._closed) {
|
|
2003
|
+
return this;
|
|
2004
|
+
}
|
|
2005
|
+
const pathArray = Array.isArray(paths) ? paths : [paths];
|
|
2006
|
+
if (this._watcher && typeof this._watcher.unwatch === "function") {
|
|
2007
|
+
this._watcher.unwatch(pathArray);
|
|
2008
|
+
}
|
|
2009
|
+
if (runtime !== "node" || !this._watcher || typeof this._watcher.unwatch !== "function") {
|
|
2010
|
+
pathArray.forEach((path) => this._untrackWatchedPath(path));
|
|
2011
|
+
}
|
|
2012
|
+
return this;
|
|
2013
|
+
}
|
|
2014
|
+
/**
|
|
2015
|
+
* Close the watcher
|
|
2016
|
+
*/
|
|
2017
|
+
async close() {
|
|
2018
|
+
if (this._closed) {
|
|
2019
|
+
return;
|
|
2020
|
+
}
|
|
2021
|
+
this._closed = true;
|
|
2022
|
+
if (this._watcher && typeof this._watcher.close === "function") {
|
|
2023
|
+
await this._watcher.close();
|
|
2024
|
+
}
|
|
2025
|
+
this.removeAllListeners();
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Get watched paths
|
|
2029
|
+
*/
|
|
2030
|
+
getWatched() {
|
|
2031
|
+
if (this._watcher && typeof this._watcher.getWatched === "function") {
|
|
2032
|
+
return this._watcher.getWatched();
|
|
2033
|
+
}
|
|
2034
|
+
const result = {};
|
|
2035
|
+
this._watched.forEach((path) => {
|
|
2036
|
+
const dir = path.substring(0, path.lastIndexOf("/")) || ".";
|
|
2037
|
+
const file = path.substring(path.lastIndexOf("/") + 1);
|
|
2038
|
+
if (!result[dir]) {
|
|
2039
|
+
result[dir] = [];
|
|
2040
|
+
}
|
|
2041
|
+
result[dir].push(file);
|
|
2042
|
+
});
|
|
2043
|
+
return result;
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Internal method to set native watcher
|
|
2047
|
+
* @internal
|
|
2048
|
+
*/
|
|
2049
|
+
_setWatcher(watcher) {
|
|
2050
|
+
this._watcher = watcher;
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Internal method to track watched paths
|
|
2054
|
+
* @internal
|
|
2055
|
+
*/
|
|
2056
|
+
_trackWatchedPath(path) {
|
|
2057
|
+
this._watched.add(path);
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Internal method to untrack watched paths
|
|
2061
|
+
* @internal
|
|
2062
|
+
*/
|
|
2063
|
+
_untrackWatchedPath(path) {
|
|
2064
|
+
this._watched.delete(path);
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Internal method to read closed state
|
|
2068
|
+
* @internal
|
|
2069
|
+
*/
|
|
2070
|
+
_isClosed() {
|
|
2071
|
+
return this._closed;
|
|
2072
|
+
}
|
|
2073
|
+
};
|
|
2074
|
+
|
|
2075
|
+
// src/server/chokidar/watch.ts
|
|
2076
|
+
function createWatchMap(paths) {
|
|
2077
|
+
const watchMap = /* @__PURE__ */ new Map();
|
|
2078
|
+
paths.forEach((path) => {
|
|
2079
|
+
const baseDir = getBaseDirectory(path);
|
|
2080
|
+
if (!watchMap.has(baseDir)) {
|
|
2081
|
+
watchMap.set(baseDir, []);
|
|
2082
|
+
}
|
|
2083
|
+
watchMap.get(baseDir).push(path);
|
|
2084
|
+
});
|
|
2085
|
+
return watchMap;
|
|
2086
|
+
}
|
|
2087
|
+
function setupDenoWatch(watcher, watchMap, pathArray) {
|
|
2088
|
+
const baseDirs = Array.from(watchMap.keys());
|
|
2089
|
+
const allPatterns = Array.from(watchMap.values()).flat();
|
|
2090
|
+
(async () => {
|
|
2091
|
+
try {
|
|
2092
|
+
const denoWatcher = Deno.watchFs(baseDirs);
|
|
2093
|
+
for await (const event of denoWatcher) {
|
|
2094
|
+
if (watcher._isClosed()) break;
|
|
2095
|
+
for (const path of event.paths) {
|
|
2096
|
+
const normalizedPath = normalizePath2(path);
|
|
2097
|
+
if (!matchesAnyPattern(normalizedPath, allPatterns)) continue;
|
|
2098
|
+
switch (event.kind) {
|
|
2099
|
+
case "create":
|
|
2100
|
+
emitEvent(watcher, "add", path);
|
|
2101
|
+
break;
|
|
2102
|
+
case "modify":
|
|
2103
|
+
emitEvent(watcher, "change", path);
|
|
2104
|
+
break;
|
|
2105
|
+
case "remove":
|
|
2106
|
+
emitEvent(watcher, "unlink", path);
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
} catch (error) {
|
|
2112
|
+
if (!watcher._isClosed()) {
|
|
2113
|
+
watcher.emit("error", error);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
})();
|
|
2117
|
+
pathArray.forEach((path) => watcher.add(path));
|
|
2118
|
+
queueMicrotask(() => watcher.emit("ready"));
|
|
2119
|
+
}
|
|
2120
|
+
function watch(paths, options) {
|
|
2121
|
+
const watcher = new FSWatcher(options);
|
|
2122
|
+
const pathArray = Array.isArray(paths) ? paths : [paths];
|
|
2123
|
+
const watchMap = createWatchMap(pathArray);
|
|
2124
|
+
if (runtime === "node" || runtime === "bun") {
|
|
2125
|
+
const fs2 = __require("fs");
|
|
2126
|
+
watchMap.forEach((patterns, baseDir) => setupFsWatch(watcher, baseDir, patterns, fs2));
|
|
2127
|
+
} else if (runtime === "deno") {
|
|
2128
|
+
setupDenoWatch(watcher, watchMap, pathArray);
|
|
2129
|
+
}
|
|
2130
|
+
return watcher;
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// src/cli/pm/runner.ts
|
|
2134
|
+
function writePmLog(stream, message) {
|
|
2135
|
+
stream.write(`[elit pm] ${(/* @__PURE__ */ new Date()).toISOString()} ${message}${EOL}`);
|
|
2136
|
+
}
|
|
2137
|
+
function waitForExit(code, signal) {
|
|
2138
|
+
if (typeof code === "number") {
|
|
2139
|
+
return code;
|
|
2140
|
+
}
|
|
2141
|
+
if (signal === "SIGINT" || signal === "SIGTERM") {
|
|
2142
|
+
return 0;
|
|
2143
|
+
}
|
|
2144
|
+
return 1;
|
|
2145
|
+
}
|
|
2146
|
+
async function delay(milliseconds) {
|
|
2147
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
2148
|
+
}
|
|
2149
|
+
async function waitForProcessTermination(pid, timeoutMs) {
|
|
2150
|
+
if (!pid || !isProcessAlive(pid)) {
|
|
2151
|
+
return true;
|
|
2152
|
+
}
|
|
2153
|
+
const deadline = Date.now() + timeoutMs;
|
|
2154
|
+
while (Date.now() < deadline) {
|
|
2155
|
+
if (!isProcessAlive(pid)) {
|
|
2156
|
+
return true;
|
|
2157
|
+
}
|
|
2158
|
+
await delay(DEFAULT_PM_STOP_POLL_MS);
|
|
2159
|
+
}
|
|
2160
|
+
return !isProcessAlive(pid);
|
|
2161
|
+
}
|
|
2162
|
+
async function waitForManagedChildExit(child) {
|
|
2163
|
+
return await new Promise((resolvePromise) => {
|
|
2164
|
+
let resolved = false;
|
|
2165
|
+
child.once("error", (error) => {
|
|
2166
|
+
if (resolved) {
|
|
2167
|
+
return;
|
|
2168
|
+
}
|
|
2169
|
+
resolved = true;
|
|
2170
|
+
resolvePromise({ code: 1, signal: null, error: error instanceof Error ? error.message : String(error) });
|
|
2171
|
+
});
|
|
2172
|
+
child.once("close", (code, signal) => {
|
|
2173
|
+
if (resolved) {
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2176
|
+
resolved = true;
|
|
2177
|
+
resolvePromise({ code, signal });
|
|
2178
|
+
});
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
async function createPmWatchController(record, onChange, onError) {
|
|
2182
|
+
if (!record.watch || record.watchPaths.length === 0) {
|
|
2183
|
+
return {
|
|
2184
|
+
async close() {
|
|
2185
|
+
}
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
const watcher = watch(record.watchPaths);
|
|
2189
|
+
let debounceTimer = null;
|
|
2190
|
+
const scheduleRestart = (filePath) => {
|
|
2191
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
2192
|
+
if (isIgnoredWatchPath(normalizedPath, record.watchIgnore)) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (debounceTimer) {
|
|
2196
|
+
clearTimeout(debounceTimer);
|
|
2197
|
+
}
|
|
2198
|
+
debounceTimer = setTimeout(() => {
|
|
2199
|
+
debounceTimer = null;
|
|
2200
|
+
onChange(normalizedPath);
|
|
2201
|
+
}, record.watchDebounce);
|
|
2202
|
+
debounceTimer.unref?.();
|
|
2203
|
+
};
|
|
2204
|
+
watcher.on("add", scheduleRestart);
|
|
2205
|
+
watcher.on("change", scheduleRestart);
|
|
2206
|
+
watcher.on("unlink", scheduleRestart);
|
|
2207
|
+
watcher.on("error", (error) => onError(error instanceof Error ? error.message : String(error)));
|
|
2208
|
+
return {
|
|
2209
|
+
async close() {
|
|
2210
|
+
if (debounceTimer) {
|
|
2211
|
+
clearTimeout(debounceTimer);
|
|
2212
|
+
debounceTimer = null;
|
|
2213
|
+
}
|
|
2214
|
+
await watcher.close();
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2217
|
+
}
|
|
2218
|
+
function createPmHealthMonitor(record, onFailure, onLog) {
|
|
2219
|
+
if (!record.healthCheck) {
|
|
2220
|
+
return {
|
|
2221
|
+
stop() {
|
|
2222
|
+
}
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
const healthCheck = record.healthCheck;
|
|
2226
|
+
let stopped = false;
|
|
2227
|
+
let timer = null;
|
|
2228
|
+
let initialDelay = null;
|
|
2229
|
+
let inFlight = false;
|
|
2230
|
+
let failureCount = 0;
|
|
2231
|
+
const runHealthCheck = async () => {
|
|
2232
|
+
if (stopped || inFlight) {
|
|
2233
|
+
return;
|
|
2234
|
+
}
|
|
2235
|
+
inFlight = true;
|
|
2236
|
+
const controller = new AbortController();
|
|
2237
|
+
const timeoutId = setTimeout(() => controller.abort(), healthCheck.timeout);
|
|
2238
|
+
timeoutId.unref?.();
|
|
2239
|
+
try {
|
|
2240
|
+
const response = await fetch(healthCheck.url, {
|
|
2241
|
+
method: "GET",
|
|
2242
|
+
signal: controller.signal
|
|
2243
|
+
});
|
|
2244
|
+
if (!response.ok) {
|
|
2245
|
+
throw new Error(`health check returned ${response.status}`);
|
|
2246
|
+
}
|
|
2247
|
+
failureCount = 0;
|
|
2248
|
+
} catch (error) {
|
|
2249
|
+
failureCount += 1;
|
|
2250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2251
|
+
onLog(`health check failed (${failureCount}/${healthCheck.maxFailures}): ${message}`);
|
|
2252
|
+
if (failureCount >= healthCheck.maxFailures) {
|
|
2253
|
+
stopped = true;
|
|
2254
|
+
onFailure(`health check failed ${failureCount} times: ${message}`);
|
|
2255
|
+
}
|
|
2256
|
+
} finally {
|
|
2257
|
+
clearTimeout(timeoutId);
|
|
2258
|
+
inFlight = false;
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
initialDelay = setTimeout(() => {
|
|
2262
|
+
void runHealthCheck();
|
|
2263
|
+
timer = setInterval(() => {
|
|
2264
|
+
void runHealthCheck();
|
|
2265
|
+
}, healthCheck.interval);
|
|
2266
|
+
timer.unref?.();
|
|
2267
|
+
}, healthCheck.gracePeriod);
|
|
2268
|
+
initialDelay.unref?.();
|
|
2269
|
+
return {
|
|
2270
|
+
stop() {
|
|
2271
|
+
stopped = true;
|
|
2272
|
+
if (initialDelay) {
|
|
2273
|
+
clearTimeout(initialDelay);
|
|
2274
|
+
initialDelay = null;
|
|
2275
|
+
}
|
|
2276
|
+
if (timer) {
|
|
2277
|
+
clearInterval(timer);
|
|
2278
|
+
timer = null;
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
function readPlannedRestartRequest(state) {
|
|
2284
|
+
return state.request;
|
|
2285
|
+
}
|
|
2286
|
+
async function runManagedProcessLoop(filePath, initialRecord) {
|
|
2287
|
+
let record = initialRecord;
|
|
2288
|
+
let activeChild = null;
|
|
2289
|
+
let activeChildStopTimer = null;
|
|
2290
|
+
let stopRequested = false;
|
|
2291
|
+
const restartState = { request: null };
|
|
2292
|
+
mkdirSync3(dirname3(initialRecord.logFiles.out), { recursive: true });
|
|
2293
|
+
mkdirSync3(dirname3(initialRecord.logFiles.err), { recursive: true });
|
|
2294
|
+
const stdoutLog = createWriteStream(initialRecord.logFiles.out, { flags: "a" });
|
|
2295
|
+
const stderrLog = createWriteStream(initialRecord.logFiles.err, { flags: "a" });
|
|
2296
|
+
const persist = (mutator) => {
|
|
2297
|
+
const current = readLatestPmRecord(filePath, record);
|
|
2298
|
+
record = mutator(current);
|
|
2299
|
+
writePmRecord(filePath, record);
|
|
2300
|
+
return record;
|
|
2301
|
+
};
|
|
2302
|
+
const clearActiveChildStopTimer = () => {
|
|
2303
|
+
if (activeChildStopTimer) {
|
|
2304
|
+
clearTimeout(activeChildStopTimer);
|
|
2305
|
+
activeChildStopTimer = null;
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
const stopActiveChild = () => {
|
|
2309
|
+
if (!activeChild?.pid || !isProcessAlive(activeChild.pid)) {
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
const current = readLatestPmRecord(filePath, record);
|
|
2313
|
+
if (isPmOnlineWapkRecord(current) && activeChild.stdin && !activeChild.stdin.destroyed && activeChild.stdin.writable) {
|
|
2314
|
+
try {
|
|
2315
|
+
activeChild.stdin.end(`${PM_WAPK_ONLINE_SHUTDOWN_COMMAND}
|
|
2316
|
+
`);
|
|
2317
|
+
clearActiveChildStopTimer();
|
|
2318
|
+
activeChildStopTimer = setTimeout(() => {
|
|
2319
|
+
if (activeChild?.pid && isProcessAlive(activeChild.pid)) {
|
|
2320
|
+
writePmLog(
|
|
2321
|
+
stderrLog,
|
|
2322
|
+
`graceful WAPK online shutdown timed out after ${PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS}ms; forcing process termination`
|
|
2323
|
+
);
|
|
2324
|
+
terminateProcessTree(activeChild.pid);
|
|
2325
|
+
}
|
|
2326
|
+
}, PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS);
|
|
2327
|
+
activeChildStopTimer.unref?.();
|
|
2328
|
+
return;
|
|
2329
|
+
} catch (error) {
|
|
2330
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2331
|
+
writePmLog(stderrLog, `graceful WAPK online shutdown failed: ${message}`);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
terminateProcessTree(activeChild.pid);
|
|
2335
|
+
};
|
|
2336
|
+
const requestManagedStop = (reason) => {
|
|
2337
|
+
if (stopRequested) {
|
|
2338
|
+
return;
|
|
2339
|
+
}
|
|
2340
|
+
stopRequested = true;
|
|
2341
|
+
persist((current) => ({
|
|
2342
|
+
...current,
|
|
2343
|
+
desiredState: "stopped",
|
|
2344
|
+
status: "stopping",
|
|
2345
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2346
|
+
}));
|
|
2347
|
+
writePmLog(stdoutLog, reason);
|
|
2348
|
+
stopActiveChild();
|
|
2349
|
+
};
|
|
2350
|
+
const handleStopSignal = (signal) => {
|
|
2351
|
+
requestManagedStop(`received ${signal}, stopping managed process`);
|
|
2352
|
+
};
|
|
2353
|
+
process.on("SIGINT", handleStopSignal);
|
|
2354
|
+
process.on("SIGTERM", handleStopSignal);
|
|
2355
|
+
persist((current) => ({
|
|
2356
|
+
...current,
|
|
2357
|
+
runnerPid: process.pid,
|
|
2358
|
+
desiredState: "running",
|
|
2359
|
+
status: "starting",
|
|
2360
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2361
|
+
}));
|
|
2362
|
+
try {
|
|
2363
|
+
while (!stopRequested) {
|
|
2364
|
+
restartState.request = null;
|
|
2365
|
+
const latest = readLatestPmRecord(filePath, record);
|
|
2366
|
+
if (latest.desiredState === "stopped") {
|
|
2367
|
+
break;
|
|
2368
|
+
}
|
|
2369
|
+
let command;
|
|
2370
|
+
try {
|
|
2371
|
+
command = buildPmCommand(latest);
|
|
2372
|
+
} catch (error) {
|
|
2373
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2374
|
+
writePmLog(stderrLog, message);
|
|
2375
|
+
persist((current2) => ({
|
|
2376
|
+
...current2,
|
|
2377
|
+
status: "errored",
|
|
2378
|
+
error: message,
|
|
2379
|
+
runnerPid: void 0,
|
|
2380
|
+
childPid: void 0,
|
|
2381
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2382
|
+
}));
|
|
2383
|
+
return;
|
|
2384
|
+
}
|
|
2385
|
+
const onlineStdinShutdownEnabled = isPmOnlineWapkRecord(latest);
|
|
2386
|
+
const child = spawn2(command.command, command.args, {
|
|
2387
|
+
cwd: latest.cwd,
|
|
2388
|
+
env: {
|
|
2389
|
+
...process.env,
|
|
2390
|
+
...latest.env,
|
|
2391
|
+
...command.env,
|
|
2392
|
+
ELIT_PM_NAME: latest.name,
|
|
2393
|
+
ELIT_PM_ID: latest.id
|
|
2394
|
+
},
|
|
2395
|
+
stdio: [onlineStdinShutdownEnabled ? "pipe" : "ignore", "pipe", "pipe"],
|
|
2396
|
+
windowsHide: true,
|
|
2397
|
+
shell: command.shell
|
|
2398
|
+
});
|
|
2399
|
+
const childStartedAt = Date.now();
|
|
2400
|
+
activeChild = child;
|
|
2401
|
+
if (child.stdout) {
|
|
2402
|
+
child.stdout.pipe(stdoutLog, { end: false });
|
|
2403
|
+
}
|
|
2404
|
+
if (child.stderr) {
|
|
2405
|
+
child.stderr.pipe(stderrLog, { end: false });
|
|
2406
|
+
}
|
|
2407
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2408
|
+
persist((current2) => ({
|
|
2409
|
+
...current2,
|
|
2410
|
+
status: "online",
|
|
2411
|
+
commandPreview: command.preview,
|
|
2412
|
+
runtime: command.runtime ?? current2.runtime,
|
|
2413
|
+
runnerPid: process.pid,
|
|
2414
|
+
childPid: child.pid,
|
|
2415
|
+
startedAt,
|
|
2416
|
+
stoppedAt: void 0,
|
|
2417
|
+
error: void 0,
|
|
2418
|
+
updatedAt: startedAt
|
|
2419
|
+
}));
|
|
2420
|
+
writePmLog(stdoutLog, `started ${command.preview}${child.pid ? ` (pid ${child.pid})` : ""}`);
|
|
2421
|
+
const requestPlannedRestart = (kind, detail) => {
|
|
2422
|
+
if (stopRequested || restartState.request) {
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2425
|
+
restartState.request = { kind, detail };
|
|
2426
|
+
writePmLog(kind === "health" ? stderrLog : stdoutLog, `${kind} restart requested: ${detail}`);
|
|
2427
|
+
persist((current2) => ({
|
|
2428
|
+
...current2,
|
|
2429
|
+
status: "restarting",
|
|
2430
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2431
|
+
}));
|
|
2432
|
+
stopActiveChild();
|
|
2433
|
+
};
|
|
2434
|
+
const watchController = await createPmWatchController(
|
|
2435
|
+
latest,
|
|
2436
|
+
(changedPath) => requestPlannedRestart("watch", changedPath),
|
|
2437
|
+
(message) => writePmLog(stderrLog, `watch error: ${message}`)
|
|
2438
|
+
);
|
|
2439
|
+
const healthMonitor = createPmHealthMonitor(
|
|
2440
|
+
latest,
|
|
2441
|
+
(message) => requestPlannedRestart("health", message),
|
|
2442
|
+
(message) => writePmLog(stdoutLog, message)
|
|
2443
|
+
);
|
|
2444
|
+
const desiredStatePoller = setInterval(() => {
|
|
2445
|
+
const latestRecord = readLatestPmRecord(filePath, record);
|
|
2446
|
+
if (!stopRequested && latestRecord.desiredState === "stopped") {
|
|
2447
|
+
requestManagedStop("stop requested by PM control state");
|
|
2448
|
+
}
|
|
2449
|
+
}, DEFAULT_PM_STOP_POLL_MS);
|
|
2450
|
+
desiredStatePoller.unref?.();
|
|
2451
|
+
const exitResult = await waitForManagedChildExit(child);
|
|
2452
|
+
clearInterval(desiredStatePoller);
|
|
2453
|
+
await watchController.close();
|
|
2454
|
+
healthMonitor.stop();
|
|
2455
|
+
clearActiveChildStopTimer();
|
|
2456
|
+
activeChild = null;
|
|
2457
|
+
const exitCode = waitForExit(exitResult.code, exitResult.signal);
|
|
2458
|
+
const current = readLatestPmRecord(filePath, record);
|
|
2459
|
+
const plannedRestart = readPlannedRestartRequest(restartState);
|
|
2460
|
+
const uptime = Math.max(0, Date.now() - childStartedAt);
|
|
2461
|
+
const wasStable = current.minUptime > 0 && uptime >= current.minUptime;
|
|
2462
|
+
if (exitResult.error) {
|
|
2463
|
+
writePmLog(stderrLog, exitResult.error);
|
|
2464
|
+
} else if (!plannedRestart) {
|
|
2465
|
+
writePmLog(stdoutLog, `process exited with code ${exitCode}`);
|
|
2466
|
+
}
|
|
2467
|
+
if (stopRequested || current.desiredState === "stopped") {
|
|
2468
|
+
break;
|
|
2469
|
+
}
|
|
2470
|
+
const shouldRestartForExit = plannedRestart ? true : current.restartPolicy === "always" ? true : current.restartPolicy === "on-failure" ? exitCode !== 0 || Boolean(exitResult.error) : false;
|
|
2471
|
+
if (!shouldRestartForExit) {
|
|
2472
|
+
persist((latestRecord) => ({
|
|
2473
|
+
...latestRecord,
|
|
2474
|
+
status: exitCode === 0 && !exitResult.error ? "exited" : "errored",
|
|
2475
|
+
childPid: void 0,
|
|
2476
|
+
runnerPid: void 0,
|
|
2477
|
+
lastExitCode: exitCode,
|
|
2478
|
+
error: exitCode === 0 && !exitResult.error ? void 0 : exitResult.error ?? `Process exited with code ${exitCode}.`,
|
|
2479
|
+
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2480
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2481
|
+
}));
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
const shouldCountRestart = plannedRestart?.kind !== "watch";
|
|
2485
|
+
const baseRestartCount = wasStable ? 0 : current.restartCount ?? 0;
|
|
2486
|
+
const nextRestartCount = shouldCountRestart ? baseRestartCount + 1 : current.restartCount ?? 0;
|
|
2487
|
+
if (nextRestartCount > current.maxRestarts) {
|
|
2488
|
+
persist((latestRecord) => ({
|
|
2489
|
+
...latestRecord,
|
|
2490
|
+
status: "errored",
|
|
2491
|
+
childPid: void 0,
|
|
2492
|
+
runnerPid: void 0,
|
|
2493
|
+
restartCount: nextRestartCount,
|
|
2494
|
+
lastExitCode: exitCode,
|
|
2495
|
+
error: plannedRestart ? `Reached max restart attempts (${current.maxRestarts}) after ${plannedRestart.kind} restart requests.` : `Reached max restart attempts (${current.maxRestarts}).`,
|
|
2496
|
+
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2497
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2498
|
+
}));
|
|
2499
|
+
writePmLog(stderrLog, `max restart attempts reached (${current.maxRestarts})`);
|
|
2500
|
+
return;
|
|
2501
|
+
}
|
|
2502
|
+
persist((latestRecord) => ({
|
|
2503
|
+
...latestRecord,
|
|
2504
|
+
status: "restarting",
|
|
2505
|
+
childPid: void 0,
|
|
2506
|
+
lastExitCode: exitCode,
|
|
2507
|
+
restartCount: nextRestartCount,
|
|
2508
|
+
error: void 0,
|
|
2509
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2510
|
+
}));
|
|
2511
|
+
if (plannedRestart) {
|
|
2512
|
+
writePmLog(
|
|
2513
|
+
plannedRestart.kind === "health" ? stderrLog : stdoutLog,
|
|
2514
|
+
`restarting in ${current.restartDelay}ms after ${plannedRestart.kind}: ${plannedRestart.detail}`
|
|
2515
|
+
);
|
|
2516
|
+
} else {
|
|
2517
|
+
writePmLog(stdoutLog, `restarting in ${current.restartDelay}ms`);
|
|
2518
|
+
}
|
|
2519
|
+
await delay(current.restartDelay);
|
|
2520
|
+
}
|
|
2521
|
+
} finally {
|
|
2522
|
+
stopRequested = true;
|
|
2523
|
+
stopActiveChild();
|
|
2524
|
+
clearActiveChildStopTimer();
|
|
2525
|
+
const finalRecord = readLatestPmRecord(filePath, record);
|
|
2526
|
+
writePmRecord(filePath, {
|
|
2527
|
+
...finalRecord,
|
|
2528
|
+
desiredState: "stopped",
|
|
2529
|
+
status: finalRecord.status === "errored" ? "errored" : finalRecord.status === "exited" ? "exited" : "stopped",
|
|
2530
|
+
runnerPid: void 0,
|
|
2531
|
+
childPid: void 0,
|
|
2532
|
+
stoppedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2533
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2534
|
+
});
|
|
2535
|
+
process.off("SIGINT", handleStopSignal);
|
|
2536
|
+
process.off("SIGTERM", handleStopSignal);
|
|
2537
|
+
await new Promise((resolvePromise) => stdoutLog.end(resolvePromise));
|
|
2538
|
+
await new Promise((resolvePromise) => stderrLog.end(resolvePromise));
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
function parseRunnerArgs(args) {
|
|
2542
|
+
let dataDir;
|
|
2543
|
+
let id;
|
|
2544
|
+
for (let index = 0; index < args.length; index++) {
|
|
2545
|
+
const arg = args[index];
|
|
2546
|
+
switch (arg) {
|
|
2547
|
+
case "--data-dir":
|
|
2548
|
+
dataDir = readRequiredValue(args, ++index, "--data-dir");
|
|
2549
|
+
break;
|
|
2550
|
+
case "--id":
|
|
2551
|
+
id = readRequiredValue(args, ++index, "--id");
|
|
2552
|
+
break;
|
|
2553
|
+
default:
|
|
2554
|
+
throw new Error(`Unknown internal pm runner option: ${arg}`);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
if (!dataDir || !id) {
|
|
2558
|
+
throw new Error("Usage: elit pm __run --data-dir <dir> --id <name>");
|
|
2559
|
+
}
|
|
2560
|
+
return {
|
|
2561
|
+
dataDir: resolve5(dataDir),
|
|
2562
|
+
id
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
async function runPmRunner(args) {
|
|
2566
|
+
const options = parseRunnerArgs(args);
|
|
2567
|
+
const paths = {
|
|
2568
|
+
dataDir: options.dataDir,
|
|
2569
|
+
appsDir: join6(options.dataDir, "apps"),
|
|
2570
|
+
logsDir: join6(options.dataDir, "logs"),
|
|
2571
|
+
dumpFile: join6(options.dataDir, DEFAULT_PM_DUMP_FILE)
|
|
2572
|
+
};
|
|
2573
|
+
const match = findPmRecordMatch(paths, options.id);
|
|
2574
|
+
if (!match) {
|
|
2575
|
+
throw new Error(`PM record not found: ${options.id}`);
|
|
2576
|
+
}
|
|
2577
|
+
await runManagedProcessLoop(match.filePath, match.record);
|
|
2578
|
+
}
|
|
2579
|
+
async function stopPmMatches(matches) {
|
|
2580
|
+
let stopped = 0;
|
|
2581
|
+
for (const match of matches) {
|
|
2582
|
+
const updated = {
|
|
2583
|
+
...match.record,
|
|
2584
|
+
desiredState: "stopped",
|
|
2585
|
+
status: match.record.runnerPid ? "stopping" : "stopped",
|
|
2586
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2587
|
+
stoppedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2588
|
+
};
|
|
2589
|
+
writePmRecord(match.filePath, updated);
|
|
2590
|
+
const runnerStopped = await waitForProcessTermination(match.record.runnerPid, DEFAULT_PM_STOP_GRACE_PERIOD_MS);
|
|
2591
|
+
const childStopped = await waitForProcessTermination(
|
|
2592
|
+
match.record.childPid,
|
|
2593
|
+
runnerStopped ? DEFAULT_PM_STOP_POLL_MS : DEFAULT_PM_STOP_GRACE_PERIOD_MS
|
|
2594
|
+
);
|
|
2595
|
+
if (!runnerStopped && match.record.runnerPid && isProcessAlive(match.record.runnerPid)) {
|
|
2596
|
+
terminateProcessTree(match.record.runnerPid);
|
|
2597
|
+
await waitForProcessTermination(match.record.runnerPid, DEFAULT_PM_STOP_POLL_MS);
|
|
2598
|
+
}
|
|
2599
|
+
if (!childStopped && match.record.childPid && isProcessAlive(match.record.childPid)) {
|
|
2600
|
+
terminateProcessTree(match.record.childPid);
|
|
2601
|
+
await waitForProcessTermination(match.record.childPid, DEFAULT_PM_STOP_POLL_MS);
|
|
2602
|
+
}
|
|
2603
|
+
writePmRecord(match.filePath, {
|
|
2604
|
+
...updated,
|
|
2605
|
+
runnerPid: void 0,
|
|
2606
|
+
childPid: void 0,
|
|
2607
|
+
status: "stopped",
|
|
2608
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2609
|
+
});
|
|
2610
|
+
stopped += 1;
|
|
2611
|
+
}
|
|
2612
|
+
return stopped;
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
// src/cli/pm/commands.ts
|
|
2616
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync } from "fs";
|
|
2617
|
+
import { EOL as EOL2 } from "os";
|
|
2618
|
+
|
|
2619
|
+
// src/shares/config/constants.ts
|
|
2620
|
+
var ELIT_CONFIG_FILES = [
|
|
2621
|
+
"elit.config.ts",
|
|
2622
|
+
"elit.config.mts",
|
|
2623
|
+
"elit.config.js",
|
|
2624
|
+
"elit.config.mjs",
|
|
2625
|
+
"elit.config.cjs",
|
|
2626
|
+
"elit.config.json"
|
|
2627
|
+
];
|
|
2628
|
+
|
|
2629
|
+
// src/shares/config/env.ts
|
|
2630
|
+
init_fs();
|
|
2631
|
+
|
|
2632
|
+
// src/shares/config/utils.ts
|
|
2633
|
+
init_fs();
|
|
2634
|
+
function readFileAsString(filePath) {
|
|
2635
|
+
const contentBuffer = readFileSync2(filePath, "utf-8");
|
|
2636
|
+
return typeof contentBuffer === "string" ? contentBuffer : contentBuffer.toString("utf-8");
|
|
2637
|
+
}
|
|
2638
|
+
function normalizeRelativeImportPath(fromDirectory, targetPath) {
|
|
2639
|
+
const relativePath2 = relative(fromDirectory, targetPath).replace(/\\/g, "/");
|
|
2640
|
+
return relativePath2.startsWith(".") ? relativePath2 : `./${relativePath2}`;
|
|
2641
|
+
}
|
|
2642
|
+
async function importConfigModule(configPath) {
|
|
2643
|
+
const { pathToFileURL } = await import("url");
|
|
2644
|
+
const configModule = await import(pathToFileURL(configPath).href);
|
|
2645
|
+
return configModule.default || configModule;
|
|
2646
|
+
}
|
|
2647
|
+
async function safeCleanup(filePath) {
|
|
2648
|
+
try {
|
|
2649
|
+
const { unlinkSync: unlinkSync2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
2650
|
+
unlinkSync2(filePath);
|
|
2651
|
+
} catch {
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
// src/shares/config/loader.ts
|
|
2656
|
+
init_fs();
|
|
2657
|
+
|
|
2658
|
+
// src/shares/workspace-package/roots.ts
|
|
2659
|
+
init_fs();
|
|
2660
|
+
|
|
2661
|
+
// src/shares/workspace-package/package-json.ts
|
|
2662
|
+
init_fs();
|
|
2663
|
+
function readPackageJson(filePath) {
|
|
2664
|
+
try {
|
|
2665
|
+
const packageJsonBuffer = readFileSync2(filePath, "utf-8");
|
|
2666
|
+
const packageJsonText = typeof packageJsonBuffer === "string" ? packageJsonBuffer : packageJsonBuffer.toString("utf-8");
|
|
2667
|
+
return JSON.parse(packageJsonText);
|
|
2668
|
+
} catch {
|
|
2669
|
+
return void 0;
|
|
2670
|
+
}
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
// src/shares/workspace-package/roots.ts
|
|
2674
|
+
function findPackageDirectory(startDir, resolveMatch) {
|
|
2675
|
+
let currentDir = resolve4(startDir);
|
|
2676
|
+
while (true) {
|
|
2677
|
+
const match = resolveMatch(currentDir);
|
|
2678
|
+
if (match) {
|
|
2679
|
+
return match;
|
|
2680
|
+
}
|
|
2681
|
+
const parentDir = dirname2(currentDir);
|
|
2682
|
+
if (parentDir === currentDir) {
|
|
2683
|
+
return void 0;
|
|
2684
|
+
}
|
|
2685
|
+
currentDir = parentDir;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function findWorkspacePackageRoot(startDir, packageName) {
|
|
2689
|
+
return findPackageDirectory(startDir, (currentDir) => {
|
|
2690
|
+
const packageJsonPath = join5(currentDir, "package.json");
|
|
2691
|
+
const packageJson = existsSync4(packageJsonPath) ? readPackageJson(packageJsonPath) : void 0;
|
|
2692
|
+
return packageJson?.name === packageName ? currentDir : void 0;
|
|
2693
|
+
});
|
|
2694
|
+
}
|
|
2695
|
+
function findInstalledPackageRoot(startDir, packageName) {
|
|
2696
|
+
return findPackageDirectory(startDir, (currentDir) => {
|
|
2697
|
+
const candidate = join5(currentDir, "node_modules", ...packageName.split("/"));
|
|
2698
|
+
return existsSync4(candidate) ? candidate : void 0;
|
|
2699
|
+
});
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
// src/shares/workspace-package/resolve.ts
|
|
2703
|
+
init_fs();
|
|
2704
|
+
|
|
2705
|
+
// src/shares/workspace-package/candidates.ts
|
|
2706
|
+
function getWorkspacePackageImportCandidates(packageRoot, specifier, options = {}) {
|
|
2707
|
+
const subpath = specifier === "elit" ? "index" : specifier.slice("elit/".length);
|
|
2708
|
+
const builtCandidates = options.preferredBuiltFormat === "cjs" ? [
|
|
2709
|
+
resolve4(packageRoot, "dist", `${subpath}.cjs`),
|
|
2710
|
+
resolve4(packageRoot, "dist", `${subpath}.js`),
|
|
2711
|
+
resolve4(packageRoot, "dist", `${subpath}.mjs`)
|
|
2712
|
+
] : [
|
|
2713
|
+
resolve4(packageRoot, "dist", `${subpath}.mjs`),
|
|
2714
|
+
resolve4(packageRoot, "dist", `${subpath}.js`),
|
|
2715
|
+
resolve4(packageRoot, "dist", `${subpath}.cjs`)
|
|
2716
|
+
];
|
|
2717
|
+
const sourceCandidates = [
|
|
2718
|
+
resolve4(packageRoot, "src", `${subpath}.ts`),
|
|
2719
|
+
resolve4(packageRoot, "src", `${subpath}.tsx`)
|
|
2720
|
+
];
|
|
2721
|
+
return options.preferBuilt ? [...builtCandidates, ...sourceCandidates] : [...sourceCandidates, ...builtCandidates];
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
// src/shares/workspace-package/resolve.ts
|
|
2725
|
+
function isWorkspacePackageSpecifier(specifier) {
|
|
2726
|
+
return specifier === "elit" || specifier.startsWith("elit/");
|
|
2727
|
+
}
|
|
2728
|
+
function resolveWorkspacePackageImport(specifier, startDir, options = {}) {
|
|
2729
|
+
if (!isWorkspacePackageSpecifier(specifier)) {
|
|
2730
|
+
return void 0;
|
|
2731
|
+
}
|
|
2732
|
+
const packageRoots = /* @__PURE__ */ new Set();
|
|
2733
|
+
const workspacePackageRoot = findWorkspacePackageRoot(startDir, "elit");
|
|
2734
|
+
const installedPackageRoot = findInstalledPackageRoot(startDir, "elit");
|
|
2735
|
+
if (workspacePackageRoot) {
|
|
2736
|
+
packageRoots.add(workspacePackageRoot);
|
|
2737
|
+
}
|
|
2738
|
+
if (installedPackageRoot) {
|
|
2739
|
+
packageRoots.add(installedPackageRoot);
|
|
2740
|
+
}
|
|
2741
|
+
for (const packageRoot of packageRoots) {
|
|
2742
|
+
for (const candidate of getWorkspacePackageImportCandidates(packageRoot, specifier, options)) {
|
|
2743
|
+
if (existsSync4(candidate)) {
|
|
2744
|
+
return candidate;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
return void 0;
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
// src/shares/config/loader.ts
|
|
2752
|
+
function resolveConfigPath(cwd = process.cwd()) {
|
|
2753
|
+
for (const configFile of ELIT_CONFIG_FILES) {
|
|
2754
|
+
const configPath = resolve4(cwd, configFile);
|
|
2755
|
+
if (existsSync4(configPath)) {
|
|
2756
|
+
return configPath;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
return null;
|
|
2760
|
+
}
|
|
2761
|
+
async function loadConfig(cwd = process.cwd()) {
|
|
2762
|
+
const configPath = resolveConfigPath(cwd);
|
|
2763
|
+
if (configPath) {
|
|
2764
|
+
try {
|
|
2765
|
+
return await loadConfigFile(configPath);
|
|
2766
|
+
} catch (error) {
|
|
2767
|
+
console.error(`Error loading config file: ${configPath.split(/[/\\]/).pop()}`);
|
|
2768
|
+
console.error(error);
|
|
2769
|
+
throw error;
|
|
2770
|
+
}
|
|
2771
|
+
}
|
|
2772
|
+
return null;
|
|
2773
|
+
}
|
|
2774
|
+
async function loadConfigFile(configPath) {
|
|
2775
|
+
const ext = configPath.split(".").pop();
|
|
2776
|
+
if (ext === "json") {
|
|
2777
|
+
const content = readFileAsString(configPath);
|
|
2778
|
+
return JSON.parse(content);
|
|
2779
|
+
}
|
|
2780
|
+
if (ext === "ts" || ext === "mts") {
|
|
2781
|
+
try {
|
|
2782
|
+
const { build } = await import("esbuild");
|
|
2783
|
+
const configDir = dirname2(configPath);
|
|
2784
|
+
const tempFile = join5(configDir, `.elit-config-${Date.now()}.mjs`);
|
|
2785
|
+
const externalAllPlugin = {
|
|
2786
|
+
name: "external-all",
|
|
2787
|
+
setup(buildContext) {
|
|
2788
|
+
buildContext.onResolve({ filter: /.*/ }, (args) => {
|
|
2789
|
+
const workspacePackageImport = resolveWorkspacePackageImport(args.path, args.resolveDir || configDir, {
|
|
2790
|
+
preferBuilt: true,
|
|
2791
|
+
preferredBuiltFormat: "esm"
|
|
2792
|
+
});
|
|
2793
|
+
if (workspacePackageImport) {
|
|
2794
|
+
return {
|
|
2795
|
+
path: normalizeRelativeImportPath(configDir, workspacePackageImport),
|
|
2796
|
+
external: true
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
if (args.path.startsWith("./") || args.path.startsWith("../")) {
|
|
2800
|
+
return void 0;
|
|
2801
|
+
}
|
|
2802
|
+
if (args.path.includes("node_modules") || args.resolveDir?.includes("node_modules")) {
|
|
2803
|
+
return { path: args.path, external: true };
|
|
2804
|
+
}
|
|
2805
|
+
const knownPackages = ["esbuild", "elit", "fs", "path", "os", "vm", "crypto", "http", "https", "url", "bun"];
|
|
2806
|
+
if (knownPackages.some((pkg) => args.path === pkg || args.path.startsWith(pkg + "/"))) {
|
|
2807
|
+
return { path: args.path, external: true };
|
|
2808
|
+
}
|
|
2809
|
+
if (args.resolveDir?.includes("elit/dist") || args.path.includes("elit/dist")) {
|
|
2810
|
+
return { path: args.path, external: true };
|
|
2811
|
+
}
|
|
2812
|
+
return void 0;
|
|
2813
|
+
});
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
await build({
|
|
2817
|
+
entryPoints: [configPath],
|
|
2818
|
+
bundle: true,
|
|
2819
|
+
banner: {
|
|
2820
|
+
js: `import { createRequire as __createRequire } from 'module'; const require = __createRequire(import.meta.url);`
|
|
2821
|
+
},
|
|
2822
|
+
format: "esm",
|
|
2823
|
+
platform: "node",
|
|
2824
|
+
outfile: tempFile,
|
|
2825
|
+
write: true,
|
|
2826
|
+
target: "es2020",
|
|
2827
|
+
plugins: [externalAllPlugin],
|
|
2828
|
+
external: [
|
|
2829
|
+
"node:*",
|
|
2830
|
+
"fs",
|
|
2831
|
+
"path",
|
|
2832
|
+
"os",
|
|
2833
|
+
"vm",
|
|
2834
|
+
"crypto",
|
|
2835
|
+
"http",
|
|
2836
|
+
"https",
|
|
2837
|
+
"bun",
|
|
2838
|
+
"bun:*",
|
|
2839
|
+
"deno",
|
|
2840
|
+
"deno:*"
|
|
2841
|
+
],
|
|
2842
|
+
absWorkingDir: configDir
|
|
2843
|
+
});
|
|
2844
|
+
const config = await importConfigModule(tempFile);
|
|
2845
|
+
await safeCleanup(tempFile);
|
|
2846
|
+
return config;
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
console.error("Failed to load TypeScript config file.");
|
|
2849
|
+
console.error("You can use a .js, .mjs, or .json config file instead.");
|
|
2850
|
+
throw error;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
return await importConfigModule(configPath);
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// src/cli/pm/commands.ts
|
|
2857
|
+
async function runPmStart(args) {
|
|
2858
|
+
const parsed = parsePmStartArgs(args);
|
|
2859
|
+
const workspaceRoot = process.cwd();
|
|
2860
|
+
const config = await loadConfig(workspaceRoot);
|
|
2861
|
+
const paths = resolvePmPaths(config?.pm, workspaceRoot);
|
|
2862
|
+
const definitions = resolvePmStartDefinitions(parsed, config, workspaceRoot);
|
|
2863
|
+
const errors = [];
|
|
2864
|
+
for (const definition of definitions) {
|
|
2865
|
+
try {
|
|
2866
|
+
const record = await startManagedProcess(definition, paths);
|
|
2867
|
+
console.log(`[pm] started ${record.name} (${record.type})`);
|
|
2868
|
+
} catch (error) {
|
|
2869
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2870
|
+
errors.push(`[pm] ${definition.name}: ${message}`);
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
if (errors.length > 0) {
|
|
2874
|
+
throw new Error(errors.join(EOL2));
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
async function loadPmContext() {
|
|
2878
|
+
const workspaceRoot = process.cwd();
|
|
2879
|
+
const config = await loadConfig(workspaceRoot);
|
|
2880
|
+
return {
|
|
2881
|
+
config,
|
|
2882
|
+
paths: resolvePmPaths(config?.pm, workspaceRoot)
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
function resolveNamedMatches(paths, value) {
|
|
2886
|
+
if (value === "all") {
|
|
2887
|
+
return listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
2888
|
+
}
|
|
2889
|
+
const match = findPmRecordMatch(paths, value);
|
|
2890
|
+
return match ? [syncPmRecordLiveness(match)] : [];
|
|
2891
|
+
}
|
|
2892
|
+
function padCell(value, width) {
|
|
2893
|
+
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
2894
|
+
}
|
|
2895
|
+
function tailLogFile(filePath, lineCount) {
|
|
2896
|
+
if (!existsSync5(filePath)) {
|
|
2897
|
+
return "";
|
|
2898
|
+
}
|
|
2899
|
+
const lines = readFileSync3(filePath, "utf8").split(/\r?\n/).filter((line) => line.length > 0);
|
|
2900
|
+
return lines.slice(-lineCount).join(EOL2);
|
|
2901
|
+
}
|
|
2902
|
+
function printPmList(paths) {
|
|
2903
|
+
const matches = listPmRecordMatches(paths).map(syncPmRecordLiveness);
|
|
2904
|
+
if (matches.length === 0) {
|
|
2905
|
+
console.log("No managed processes found.");
|
|
2906
|
+
return;
|
|
2907
|
+
}
|
|
2908
|
+
const headers = [
|
|
2909
|
+
padCell("name", 20),
|
|
2910
|
+
padCell("status", 12),
|
|
2911
|
+
padCell("pid", 8),
|
|
2912
|
+
padCell("restarts", 10),
|
|
2913
|
+
padCell("type", 8),
|
|
2914
|
+
"runtime"
|
|
2915
|
+
];
|
|
2916
|
+
console.log(headers.join(" "));
|
|
2917
|
+
for (const { record } of matches) {
|
|
2918
|
+
console.log([
|
|
2919
|
+
padCell(record.name, 20),
|
|
2920
|
+
padCell(record.status, 12),
|
|
2921
|
+
padCell(record.childPid ? String(record.childPid) : "-", 8),
|
|
2922
|
+
padCell(String(record.restartCount ?? 0), 10),
|
|
2923
|
+
padCell(record.type, 8),
|
|
2924
|
+
record.runtime ?? "-"
|
|
2925
|
+
].join(" "));
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
async function runPmStop(args) {
|
|
2929
|
+
const target = args[0];
|
|
2930
|
+
if (!target) {
|
|
2931
|
+
throw new Error("Usage: elit pm stop <name|all>");
|
|
2932
|
+
}
|
|
2933
|
+
const { paths } = await loadPmContext();
|
|
2934
|
+
const matches = resolveNamedMatches(paths, target);
|
|
2935
|
+
if (matches.length === 0) {
|
|
2936
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
2937
|
+
}
|
|
2938
|
+
const count = await stopPmMatches(matches);
|
|
2939
|
+
console.log(`[pm] stopped ${count} process${count === 1 ? "" : "es"}`);
|
|
2940
|
+
}
|
|
2941
|
+
async function runPmRestart(args) {
|
|
2942
|
+
const target = args[0];
|
|
2943
|
+
if (!target) {
|
|
2944
|
+
throw new Error("Usage: elit pm restart <name|all>");
|
|
2945
|
+
}
|
|
2946
|
+
const { paths } = await loadPmContext();
|
|
2947
|
+
const matches = resolveNamedMatches(paths, target);
|
|
2948
|
+
if (matches.length === 0) {
|
|
2949
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
2950
|
+
}
|
|
2951
|
+
await stopPmMatches(matches);
|
|
2952
|
+
const restarted = [];
|
|
2953
|
+
for (const match of matches) {
|
|
2954
|
+
const definition = resolvePmAppDefinition(
|
|
2955
|
+
toPmAppConfig(match.record),
|
|
2956
|
+
{ name: match.record.name, env: {}, watchPaths: [], watchIgnore: [] },
|
|
2957
|
+
process.cwd(),
|
|
2958
|
+
match.record.source
|
|
2959
|
+
);
|
|
2960
|
+
await startManagedProcess(definition, paths);
|
|
2961
|
+
restarted.push(match.record.name);
|
|
2962
|
+
}
|
|
2963
|
+
console.log(`[pm] restarted ${restarted.join(", ")}`);
|
|
2964
|
+
}
|
|
2965
|
+
async function runPmSave() {
|
|
2966
|
+
const { paths } = await loadPmContext();
|
|
2967
|
+
ensurePmDirectories(paths);
|
|
2968
|
+
const runningApps = listPmRecordMatches(paths).map(syncPmRecordLiveness).filter((match) => match.record.desiredState === "running" && (match.record.status === "starting" || match.record.status === "online" || match.record.status === "restarting")).map((match) => toSavedAppDefinition(match.record));
|
|
2969
|
+
writePmDumpFile(paths.dumpFile, runningApps);
|
|
2970
|
+
console.log(`[pm] saved ${runningApps.length} process${runningApps.length === 1 ? "" : "es"} to ${paths.dumpFile}`);
|
|
2971
|
+
}
|
|
2972
|
+
async function runPmResurrect() {
|
|
2973
|
+
const { paths } = await loadPmContext();
|
|
2974
|
+
if (!existsSync5(paths.dumpFile)) {
|
|
2975
|
+
throw new Error(`PM dump file not found: ${paths.dumpFile}`);
|
|
2976
|
+
}
|
|
2977
|
+
const dump = readPmDumpFile(paths.dumpFile);
|
|
2978
|
+
if (dump.apps.length === 0) {
|
|
2979
|
+
console.log("[pm] dump file is empty, nothing to resurrect");
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
const errors = [];
|
|
2983
|
+
let restored = 0;
|
|
2984
|
+
for (const app of dump.apps) {
|
|
2985
|
+
try {
|
|
2986
|
+
const definition = resolvePmAppDefinition(
|
|
2987
|
+
toSavedPmAppConfig(app),
|
|
2988
|
+
{ name: app.name, env: {}, watchPaths: [], watchIgnore: [] },
|
|
2989
|
+
process.cwd(),
|
|
2990
|
+
"cli"
|
|
2991
|
+
);
|
|
2992
|
+
await startManagedProcess(definition, paths);
|
|
2993
|
+
restored += 1;
|
|
2994
|
+
} catch (error) {
|
|
2995
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2996
|
+
errors.push(`[pm] ${app.name}: ${message}`);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
if (errors.length > 0) {
|
|
3000
|
+
throw new Error([`[pm] resurrected ${restored} process${restored === 1 ? "" : "es"}`, ...errors].join(EOL2));
|
|
3001
|
+
}
|
|
3002
|
+
console.log(`[pm] resurrected ${restored} process${restored === 1 ? "" : "es"} from ${paths.dumpFile}`);
|
|
3003
|
+
}
|
|
3004
|
+
async function runPmDelete(args) {
|
|
3005
|
+
const target = args[0];
|
|
3006
|
+
if (!target) {
|
|
3007
|
+
throw new Error("Usage: elit pm delete <name|all>");
|
|
3008
|
+
}
|
|
3009
|
+
const { paths } = await loadPmContext();
|
|
3010
|
+
const matches = resolveNamedMatches(paths, target);
|
|
3011
|
+
if (matches.length === 0) {
|
|
3012
|
+
throw new Error(`No managed process found for: ${target}`);
|
|
3013
|
+
}
|
|
3014
|
+
await stopPmMatches(matches);
|
|
3015
|
+
for (const match of matches) {
|
|
3016
|
+
if (existsSync5(match.record.logFiles.out)) {
|
|
3017
|
+
rmSync(match.record.logFiles.out, { force: true });
|
|
3018
|
+
}
|
|
3019
|
+
if (existsSync5(match.record.logFiles.err)) {
|
|
3020
|
+
rmSync(match.record.logFiles.err, { force: true });
|
|
3021
|
+
}
|
|
3022
|
+
rmSync(match.filePath, { force: true });
|
|
3023
|
+
}
|
|
3024
|
+
console.log(`[pm] deleted ${matches.length} process${matches.length === 1 ? "" : "es"}`);
|
|
3025
|
+
}
|
|
3026
|
+
async function runPmLogs(args) {
|
|
3027
|
+
if (args.length === 0) {
|
|
3028
|
+
throw new Error("Usage: elit pm logs <name> [--lines <n>] [--stderr]");
|
|
3029
|
+
}
|
|
3030
|
+
let name;
|
|
3031
|
+
let lineCount = DEFAULT_LOG_LINES;
|
|
3032
|
+
let stderrOnly = false;
|
|
3033
|
+
for (let index = 0; index < args.length; index++) {
|
|
3034
|
+
const arg = args[index];
|
|
3035
|
+
switch (arg) {
|
|
3036
|
+
case "--lines":
|
|
3037
|
+
lineCount = normalizeIntegerOption(readRequiredValue(args, ++index, "--lines"), "--lines", 1);
|
|
3038
|
+
break;
|
|
3039
|
+
case "--stderr":
|
|
3040
|
+
stderrOnly = true;
|
|
3041
|
+
break;
|
|
3042
|
+
default:
|
|
3043
|
+
if (arg.startsWith("-")) {
|
|
3044
|
+
throw new Error(`Unknown pm logs option: ${arg}`);
|
|
3045
|
+
}
|
|
3046
|
+
if (name) {
|
|
3047
|
+
throw new Error("pm logs accepts exactly one process name.");
|
|
3048
|
+
}
|
|
3049
|
+
name = arg;
|
|
3050
|
+
break;
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
if (!name) {
|
|
3054
|
+
throw new Error("Usage: elit pm logs <name> [--lines <n>] [--stderr]");
|
|
3055
|
+
}
|
|
3056
|
+
const { paths } = await loadPmContext();
|
|
3057
|
+
const match = findPmRecordMatch(paths, name);
|
|
3058
|
+
if (!match) {
|
|
3059
|
+
throw new Error(`No managed process found for: ${name}`);
|
|
3060
|
+
}
|
|
3061
|
+
const stdoutContent = stderrOnly ? "" : tailLogFile(match.record.logFiles.out, lineCount);
|
|
3062
|
+
const stderrContent = tailLogFile(match.record.logFiles.err, lineCount);
|
|
3063
|
+
if (!stderrOnly) {
|
|
3064
|
+
console.log(`== stdout: ${match.record.logFiles.out} ==`);
|
|
3065
|
+
console.log(stdoutContent || "(empty)");
|
|
3066
|
+
}
|
|
3067
|
+
console.log(`== stderr: ${match.record.logFiles.err} ==`);
|
|
3068
|
+
console.log(stderrContent || "(empty)");
|
|
3069
|
+
}
|
|
3070
|
+
async function runPmCommand(args) {
|
|
3071
|
+
if (args.length === 0 || args[0] === "help" || args.includes("--help") || args.includes("-h")) {
|
|
3072
|
+
printPmHelp();
|
|
3073
|
+
return;
|
|
3074
|
+
}
|
|
3075
|
+
const command = args[0];
|
|
3076
|
+
switch (command) {
|
|
3077
|
+
case "start":
|
|
3078
|
+
await runPmStart(args.slice(1));
|
|
3079
|
+
return;
|
|
3080
|
+
case "list":
|
|
3081
|
+
case "ls": {
|
|
3082
|
+
const { paths } = await loadPmContext();
|
|
3083
|
+
printPmList(paths);
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
case "stop":
|
|
3087
|
+
await runPmStop(args.slice(1));
|
|
3088
|
+
return;
|
|
3089
|
+
case "restart":
|
|
3090
|
+
await runPmRestart(args.slice(1));
|
|
3091
|
+
return;
|
|
3092
|
+
case "delete":
|
|
3093
|
+
case "remove":
|
|
3094
|
+
case "rm":
|
|
3095
|
+
await runPmDelete(args.slice(1));
|
|
3096
|
+
return;
|
|
3097
|
+
case "save":
|
|
3098
|
+
await runPmSave();
|
|
3099
|
+
return;
|
|
3100
|
+
case "resurrect":
|
|
3101
|
+
await runPmResurrect();
|
|
3102
|
+
return;
|
|
3103
|
+
case "logs":
|
|
3104
|
+
await runPmLogs(args.slice(1));
|
|
3105
|
+
return;
|
|
3106
|
+
case "__run":
|
|
3107
|
+
await runPmRunner(args.slice(1));
|
|
3108
|
+
return;
|
|
3109
|
+
default:
|
|
3110
|
+
throw new Error(`Unknown pm command: ${command}`);
|
|
3111
|
+
}
|
|
3112
|
+
}
|
|
3113
|
+
export {
|
|
3114
|
+
DEFAULT_HEALTHCHECK_GRACE_PERIOD,
|
|
3115
|
+
DEFAULT_HEALTHCHECK_INTERVAL,
|
|
3116
|
+
DEFAULT_HEALTHCHECK_MAX_FAILURES,
|
|
3117
|
+
DEFAULT_HEALTHCHECK_TIMEOUT,
|
|
3118
|
+
DEFAULT_LOG_LINES,
|
|
3119
|
+
DEFAULT_MAX_RESTARTS,
|
|
3120
|
+
DEFAULT_MIN_UPTIME,
|
|
3121
|
+
DEFAULT_PM_DATA_DIR,
|
|
3122
|
+
DEFAULT_PM_DUMP_FILE,
|
|
3123
|
+
DEFAULT_PM_STOP_GRACE_PERIOD_MS,
|
|
3124
|
+
DEFAULT_PM_STOP_POLL_MS,
|
|
3125
|
+
DEFAULT_RESTART_DELAY,
|
|
3126
|
+
DEFAULT_WATCH_DEBOUNCE,
|
|
3127
|
+
DEFAULT_WATCH_IGNORE,
|
|
3128
|
+
PM_RECORD_EXTENSION,
|
|
3129
|
+
PM_WAPK_ONLINE_SHUTDOWN_COMMAND,
|
|
3130
|
+
PM_WAPK_ONLINE_SHUTDOWN_TIMEOUT_MS,
|
|
3131
|
+
PM_WAPK_ONLINE_STDIN_SHUTDOWN_ENV,
|
|
3132
|
+
SIMPLE_PREVIEW_SEGMENT,
|
|
3133
|
+
SUPPORTED_FILE_EXTENSIONS,
|
|
3134
|
+
appendPmWapkRunArgs,
|
|
3135
|
+
buildGoogleDriveWapkSpecifier,
|
|
3136
|
+
buildPmCommand,
|
|
3137
|
+
buildPmWapkPreview,
|
|
3138
|
+
countDefinedPmWapkSources,
|
|
3139
|
+
deriveDefaultWatchPaths,
|
|
3140
|
+
ensurePmDirectories,
|
|
3141
|
+
findPmRecordMatch,
|
|
3142
|
+
getPmRecordPath,
|
|
3143
|
+
hasPmGoogleDriveConfig,
|
|
3144
|
+
hasPmWapkRunConfig,
|
|
3145
|
+
isIgnoredWatchPath,
|
|
3146
|
+
isPmOnlineWapkRecord,
|
|
3147
|
+
isPmWapkOnlineRunConfig,
|
|
3148
|
+
isProcessAlive,
|
|
3149
|
+
isRemoteWapkArchiveSpecifier,
|
|
3150
|
+
isTypescriptFile,
|
|
3151
|
+
isWapkArchiveSpecifier,
|
|
3152
|
+
listPmRecordMatches,
|
|
3153
|
+
looksLikeManagedFile,
|
|
3154
|
+
matchesGlobPattern,
|
|
3155
|
+
mergePmWapkRunConfig,
|
|
3156
|
+
normalizeEnvMap,
|
|
3157
|
+
normalizeHealthCheckConfig,
|
|
3158
|
+
normalizeIntegerOption,
|
|
3159
|
+
normalizeNonEmptyString,
|
|
3160
|
+
normalizePmRestartPolicy,
|
|
3161
|
+
normalizePmRuntime,
|
|
3162
|
+
normalizeResolvedWatchIgnorePaths,
|
|
3163
|
+
normalizeResolvedWatchPaths,
|
|
3164
|
+
normalizeStringArray,
|
|
3165
|
+
normalizeWatchIgnorePatterns,
|
|
3166
|
+
normalizeWatchPatterns,
|
|
3167
|
+
parsePmEnvEntry,
|
|
3168
|
+
parsePmStartArgs,
|
|
3169
|
+
printPmHelp,
|
|
3170
|
+
quoteCommandSegment,
|
|
3171
|
+
readLatestPmRecord,
|
|
3172
|
+
readPmDumpFile,
|
|
3173
|
+
readPmRecord,
|
|
3174
|
+
readRequiredValue,
|
|
3175
|
+
resolvePmAppDefinition,
|
|
3176
|
+
resolvePmPaths,
|
|
3177
|
+
resolvePmStartDefinitions,
|
|
3178
|
+
resolvePmWapkSource,
|
|
3179
|
+
resolvePmWapkSourceToken,
|
|
3180
|
+
runManagedProcessLoop,
|
|
3181
|
+
runPmCommand,
|
|
3182
|
+
runPmRunner,
|
|
3183
|
+
sanitizePmProcessName,
|
|
3184
|
+
startManagedProcess,
|
|
3185
|
+
stopPmMatches,
|
|
3186
|
+
stripPmWapkSourceFromRunConfig,
|
|
3187
|
+
syncPmRecordLiveness,
|
|
3188
|
+
terminateProcessTree,
|
|
3189
|
+
toPmAppConfig,
|
|
3190
|
+
toSavedAppDefinition,
|
|
3191
|
+
toSavedPmAppConfig,
|
|
3192
|
+
toWatchGlob,
|
|
3193
|
+
waitForProcessTermination,
|
|
3194
|
+
writePmDumpFile,
|
|
3195
|
+
writePmRecord
|
|
3196
|
+
};
|