issue-flow 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-OX3KPHE4.js → agent-AT64RXTA.js} +11 -11
- package/dist/{analyze-WIND2SEB.js → analyze-O2PYDIFK.js} +18 -18
- package/dist/{apply-JNOI2LR4.js → apply-SSU6QCD4.js} +11 -11
- package/dist/{bench-B4VZON7E.js → bench-ODFPAGW6.js} +16 -16
- package/dist/{chunk-QD4VOILZ.js → chunk-2ZAM45PZ.js} +9 -9
- package/dist/{chunk-PBTA3QQX.js → chunk-4RXYFWP7.js} +3 -3
- package/dist/{chunk-CGKLE6WR.js → chunk-5ZKBIXET.js} +12 -12
- package/dist/{chunk-WB2X37MO.js → chunk-6E2PEBFT.js} +7 -7
- package/dist/{chunk-UJ3L2MS7.js → chunk-6FXOU2KR.js} +6 -6
- package/dist/{chunk-RVO7L4FX.js → chunk-6SRE4KD7.js} +4 -4
- package/dist/{chunk-KMRFRXIT.js → chunk-A35HE4V7.js} +3 -3
- package/dist/{chunk-6BENHR2I.js → chunk-ARWNYXFL.js} +12 -12
- package/dist/{chunk-3WFZETIM.js → chunk-AYYMW33T.js} +2 -2
- package/dist/{chunk-LFEQIZLB.js → chunk-BMHGRYL6.js} +396 -64
- package/dist/chunk-BMHGRYL6.js.map +1 -0
- package/dist/{chunk-PZTWU277.js → chunk-F4OK7KEP.js} +7 -7
- package/dist/{chunk-SZMHCEYV.js → chunk-FK4IDY6P.js} +9 -9
- package/dist/{chunk-BEZ5BA5U.js → chunk-GSZCQUCY.js} +2 -2
- package/dist/{chunk-X44FKNKP.js → chunk-HGSYD57B.js} +34 -30
- package/dist/chunk-HGSYD57B.js.map +1 -0
- package/dist/{chunk-2IQ6M4ZE.js → chunk-KAHQ6F5C.js} +11 -11
- package/dist/{chunk-F6TOYSYY.js → chunk-MEFY4B7T.js} +9 -9
- package/dist/{chunk-BRCNQXYQ.js → chunk-MGCOQU6N.js} +4 -4
- package/dist/{chunk-KT7JKOXB.js → chunk-MROS3SKL.js} +3 -3
- package/dist/{chunk-TYRNTSPJ.js → chunk-NVZMBXTE.js} +3 -3
- package/dist/{chunk-PHW2YV6N.js → chunk-PCN7ZQND.js} +5 -5
- package/dist/{chunk-76MMPJ53.js → chunk-QRCGW2ZO.js} +5 -5
- package/dist/{chunk-OR43364P.js → chunk-RPXDBX6R.js} +3 -3
- package/dist/{chunk-JN6D3MY6.js → chunk-RWEXK55D.js} +2 -2
- package/dist/{chunk-67YIU2VB.js → chunk-TBDP6QPJ.js} +4 -4
- package/dist/{chunk-G5EK2XRN.js → chunk-UNQYMR3H.js} +2 -2
- package/dist/{chunk-QK7WUO7A.js → chunk-VR34TXHF.js} +6 -6
- package/dist/{chunk-QJ5UZAH5.js → chunk-WM3Y7Q4A.js} +10 -10
- package/dist/{chunk-LBGHI4S2.js → chunk-XDKGA6UX.js} +4 -2
- package/dist/{chunk-LBGHI4S2.js.map → chunk-XDKGA6UX.js.map} +1 -1
- package/dist/{chunk-REO3PXXO.js → chunk-XG2NYDRA.js} +3 -3
- package/dist/{chunk-LEW4QM74.js → chunk-XLVGPXNL.js} +2 -2
- package/dist/{chunk-J3SOTUVI.js → chunk-XQHFC62U.js} +2 -2
- package/dist/{chunk-QZVKRDJY.js → chunk-XRLRKTQA.js} +10 -10
- package/dist/{chunk-B72RQZBV.js → chunk-ZFIXEYUJ.js} +21 -18
- package/dist/chunk-ZFIXEYUJ.js.map +1 -0
- package/dist/{chunk-V7MO5EDS.js → chunk-ZPIVCM7Q.js} +3 -3
- package/dist/{chunk-OOVG2KBY.js → chunk-ZXPASFHG.js} +2 -2
- package/dist/cli.js +56 -65
- package/dist/cli.js.map +1 -1
- package/dist/{config-HMFMCY2M.js → config-UMP6DBJB.js} +10 -10
- package/dist/{conventions-NJF2OPVQ.js → conventions-DKH7AE4T.js} +12 -12
- package/dist/{diagnostics-THH42ZMP.js → diagnostics-GOGCLTRS.js} +6 -6
- package/dist/execute-E3BRMDAQ.js +35 -0
- package/dist/{generate-FW5C6NP7.js → generate-AGBT7EHX.js} +18 -18
- package/dist/{git-W72QQ6GT.js → git-AC542GU2.js} +2 -2
- package/dist/init-LLBVJNPE.js +24 -0
- package/dist/{operations-BCFOBH42.js → operations-JQYK2EMT.js} +11 -11
- package/dist/{permissions-4AKNQZHK.js → permissions-6HL52K54.js} +3 -3
- package/dist/plan-HSLWDJEZ.js +34 -0
- package/dist/{policy-DT6Q6ZVM.js → policy-IMRXGG7X.js} +11 -11
- package/dist/pr-6TCZXG6K.js +39 -0
- package/dist/pr-review-6MWJO2NP.js +32 -0
- package/dist/prd-D6YDYAFX.js +33 -0
- package/dist/{ps-RFORTCUD.js → ps-KUUZ6TUL.js} +9 -9
- package/dist/{recorder-KCCPMKQG.js → recorder-BJ2KIAWT.js} +5 -5
- package/dist/{registry-GLPXOMFG.js → registry-SB7XTMGE.js} +5 -5
- package/dist/{resume-M5RLKP4I.js → resume-QNLQSCR5.js} +34 -34
- package/dist/review-3KJ4BH2V.js +35 -0
- package/dist/{routing-NNT5WKGX.js → routing-TN7GLMGU.js} +11 -11
- package/dist/run-5EHIFF2V.js +54 -0
- package/dist/{state-manager-GQH32C44.js → state-manager-ZEPWTCA6.js} +3 -3
- package/dist/{usage-V2CHE5PY.js → usage-QL563I7F.js} +9 -9
- package/dist/{web-EA3KGNFN.js → web-PPSMNTQ6.js} +18 -57
- package/dist/{web-EA3KGNFN.js.map → web-PPSMNTQ6.js.map} +1 -1
- package/package.json +1 -1
- package/web/AGENTS.md +4 -0
- package/web/public/app.js +29 -10
- package/dist/chunk-B72RQZBV.js.map +0 -1
- package/dist/chunk-LFEQIZLB.js.map +0 -1
- package/dist/chunk-X44FKNKP.js.map +0 -1
- package/dist/execute-KFNNZZHI.js +0 -35
- package/dist/init-HTN4V67Q.js +0 -24
- package/dist/plan-SIZLGA4V.js +0 -34
- package/dist/pr-OMWCGUOW.js +0 -39
- package/dist/pr-review-N24CZPKR.js +0 -32
- package/dist/prd-5OFTZAHJ.js +0 -33
- package/dist/review-YM4JAABY.js +0 -35
- package/dist/run-PZUKHPQK.js +0 -54
- /package/dist/{agent-OX3KPHE4.js.map → agent-AT64RXTA.js.map} +0 -0
- /package/dist/{analyze-WIND2SEB.js.map → analyze-O2PYDIFK.js.map} +0 -0
- /package/dist/{apply-JNOI2LR4.js.map → apply-SSU6QCD4.js.map} +0 -0
- /package/dist/{bench-B4VZON7E.js.map → bench-ODFPAGW6.js.map} +0 -0
- /package/dist/{chunk-QD4VOILZ.js.map → chunk-2ZAM45PZ.js.map} +0 -0
- /package/dist/{chunk-PBTA3QQX.js.map → chunk-4RXYFWP7.js.map} +0 -0
- /package/dist/{chunk-CGKLE6WR.js.map → chunk-5ZKBIXET.js.map} +0 -0
- /package/dist/{chunk-WB2X37MO.js.map → chunk-6E2PEBFT.js.map} +0 -0
- /package/dist/{chunk-UJ3L2MS7.js.map → chunk-6FXOU2KR.js.map} +0 -0
- /package/dist/{chunk-RVO7L4FX.js.map → chunk-6SRE4KD7.js.map} +0 -0
- /package/dist/{chunk-KMRFRXIT.js.map → chunk-A35HE4V7.js.map} +0 -0
- /package/dist/{chunk-6BENHR2I.js.map → chunk-ARWNYXFL.js.map} +0 -0
- /package/dist/{chunk-3WFZETIM.js.map → chunk-AYYMW33T.js.map} +0 -0
- /package/dist/{chunk-PZTWU277.js.map → chunk-F4OK7KEP.js.map} +0 -0
- /package/dist/{chunk-SZMHCEYV.js.map → chunk-FK4IDY6P.js.map} +0 -0
- /package/dist/{chunk-BEZ5BA5U.js.map → chunk-GSZCQUCY.js.map} +0 -0
- /package/dist/{chunk-2IQ6M4ZE.js.map → chunk-KAHQ6F5C.js.map} +0 -0
- /package/dist/{chunk-F6TOYSYY.js.map → chunk-MEFY4B7T.js.map} +0 -0
- /package/dist/{chunk-BRCNQXYQ.js.map → chunk-MGCOQU6N.js.map} +0 -0
- /package/dist/{chunk-KT7JKOXB.js.map → chunk-MROS3SKL.js.map} +0 -0
- /package/dist/{chunk-TYRNTSPJ.js.map → chunk-NVZMBXTE.js.map} +0 -0
- /package/dist/{chunk-PHW2YV6N.js.map → chunk-PCN7ZQND.js.map} +0 -0
- /package/dist/{chunk-76MMPJ53.js.map → chunk-QRCGW2ZO.js.map} +0 -0
- /package/dist/{chunk-OR43364P.js.map → chunk-RPXDBX6R.js.map} +0 -0
- /package/dist/{chunk-JN6D3MY6.js.map → chunk-RWEXK55D.js.map} +0 -0
- /package/dist/{chunk-67YIU2VB.js.map → chunk-TBDP6QPJ.js.map} +0 -0
- /package/dist/{chunk-G5EK2XRN.js.map → chunk-UNQYMR3H.js.map} +0 -0
- /package/dist/{chunk-QK7WUO7A.js.map → chunk-VR34TXHF.js.map} +0 -0
- /package/dist/{chunk-QJ5UZAH5.js.map → chunk-WM3Y7Q4A.js.map} +0 -0
- /package/dist/{chunk-REO3PXXO.js.map → chunk-XG2NYDRA.js.map} +0 -0
- /package/dist/{chunk-LEW4QM74.js.map → chunk-XLVGPXNL.js.map} +0 -0
- /package/dist/{chunk-J3SOTUVI.js.map → chunk-XQHFC62U.js.map} +0 -0
- /package/dist/{chunk-QZVKRDJY.js.map → chunk-XRLRKTQA.js.map} +0 -0
- /package/dist/{chunk-V7MO5EDS.js.map → chunk-ZPIVCM7Q.js.map} +0 -0
- /package/dist/{chunk-OOVG2KBY.js.map → chunk-ZXPASFHG.js.map} +0 -0
- /package/dist/{config-HMFMCY2M.js.map → config-UMP6DBJB.js.map} +0 -0
- /package/dist/{conventions-NJF2OPVQ.js.map → conventions-DKH7AE4T.js.map} +0 -0
- /package/dist/{diagnostics-THH42ZMP.js.map → diagnostics-GOGCLTRS.js.map} +0 -0
- /package/dist/{execute-KFNNZZHI.js.map → execute-E3BRMDAQ.js.map} +0 -0
- /package/dist/{generate-FW5C6NP7.js.map → generate-AGBT7EHX.js.map} +0 -0
- /package/dist/{git-W72QQ6GT.js.map → git-AC542GU2.js.map} +0 -0
- /package/dist/{init-HTN4V67Q.js.map → init-LLBVJNPE.js.map} +0 -0
- /package/dist/{operations-BCFOBH42.js.map → operations-JQYK2EMT.js.map} +0 -0
- /package/dist/{permissions-4AKNQZHK.js.map → permissions-6HL52K54.js.map} +0 -0
- /package/dist/{plan-SIZLGA4V.js.map → plan-HSLWDJEZ.js.map} +0 -0
- /package/dist/{policy-DT6Q6ZVM.js.map → policy-IMRXGG7X.js.map} +0 -0
- /package/dist/{pr-OMWCGUOW.js.map → pr-6TCZXG6K.js.map} +0 -0
- /package/dist/{pr-review-N24CZPKR.js.map → pr-review-6MWJO2NP.js.map} +0 -0
- /package/dist/{prd-5OFTZAHJ.js.map → prd-D6YDYAFX.js.map} +0 -0
- /package/dist/{ps-RFORTCUD.js.map → ps-KUUZ6TUL.js.map} +0 -0
- /package/dist/{recorder-KCCPMKQG.js.map → recorder-BJ2KIAWT.js.map} +0 -0
- /package/dist/{registry-GLPXOMFG.js.map → registry-SB7XTMGE.js.map} +0 -0
- /package/dist/{resume-M5RLKP4I.js.map → resume-QNLQSCR5.js.map} +0 -0
- /package/dist/{review-YM4JAABY.js.map → review-3KJ4BH2V.js.map} +0 -0
- /package/dist/{routing-NNT5WKGX.js.map → routing-TN7GLMGU.js.map} +0 -0
- /package/dist/{run-PZUKHPQK.js.map → run-5EHIFF2V.js.map} +0 -0
- /package/dist/{state-manager-GQH32C44.js.map → state-manager-ZEPWTCA6.js.map} +0 -0
- /package/dist/{usage-V2CHE5PY.js.map → usage-QL563I7F.js.map} +0 -0
|
@@ -1,41 +1,43 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
writeAgentPreference
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6FXOU2KR.js";
|
|
5
5
|
import {
|
|
6
6
|
resolvePackageDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A35HE4V7.js";
|
|
8
8
|
import {
|
|
9
9
|
printInfo,
|
|
10
10
|
printWarning
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NVZMBXTE.js";
|
|
12
12
|
import {
|
|
13
13
|
NullPublisher
|
|
14
14
|
} from "./chunk-6QYMSNVA.js";
|
|
15
15
|
import {
|
|
16
16
|
readDiagnostics
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-AYYMW33T.js";
|
|
18
18
|
import {
|
|
19
19
|
getGlobalRoot
|
|
20
20
|
} from "./chunk-HAKDLR6N.js";
|
|
21
21
|
import {
|
|
22
22
|
isoNow
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XQHFC62U.js";
|
|
24
24
|
import {
|
|
25
25
|
isAgentPhase,
|
|
26
26
|
isAgentProviderId,
|
|
27
27
|
isProcessAlive,
|
|
28
28
|
webLockSchema
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-XDKGA6UX.js";
|
|
30
30
|
|
|
31
31
|
// src/web/lock.ts
|
|
32
32
|
import { spawn as nodeSpawn } from "child_process";
|
|
33
|
-
import {
|
|
33
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
34
|
+
import { mkdir, readFile as readFile2, rm, writeFile } from "fs/promises";
|
|
34
35
|
import { join as join2 } from "path";
|
|
35
36
|
import chalk2 from "chalk";
|
|
37
|
+
import { execa } from "execa";
|
|
36
38
|
|
|
37
39
|
// src/web/server.ts
|
|
38
|
-
import { createHash } from "crypto";
|
|
40
|
+
import { createHash, randomUUID } from "crypto";
|
|
39
41
|
import { readFile } from "fs/promises";
|
|
40
42
|
import { createServer } from "http";
|
|
41
43
|
import { createRequire } from "module";
|
|
@@ -44,11 +46,13 @@ import chalk from "chalk";
|
|
|
44
46
|
var require2 = createRequire(import.meta.url);
|
|
45
47
|
var SESSION_LIST_DESCRIPTION_MAX = 280;
|
|
46
48
|
function readPackageVersion() {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
for (const candidate of ["../package.json", "../../package.json"]) {
|
|
50
|
+
try {
|
|
51
|
+
return require2(candidate).version;
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
51
54
|
}
|
|
55
|
+
return "0.0.0";
|
|
52
56
|
}
|
|
53
57
|
function truncateSessionDescription(text, max = SESSION_LIST_DESCRIPTION_MAX) {
|
|
54
58
|
if (text === null || text === void 0) return null;
|
|
@@ -99,8 +103,9 @@ async function loadStaticAssets(publicDir) {
|
|
|
99
103
|
}
|
|
100
104
|
return assets;
|
|
101
105
|
}
|
|
102
|
-
function baseHeaders(res) {
|
|
106
|
+
function baseHeaders(res, instanceId) {
|
|
103
107
|
res.setHeader("Cache-Control", "no-store");
|
|
108
|
+
res.setHeader("X-Issue-Flow-Instance", instanceId);
|
|
104
109
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
105
110
|
res.setHeader("Referrer-Policy", "no-referrer");
|
|
106
111
|
res.setHeader("X-Robots-Tag", "noindex, nofollow");
|
|
@@ -131,6 +136,7 @@ async function startWebServer(options) {
|
|
|
131
136
|
const info = options.info ?? printInfo;
|
|
132
137
|
const warn = options.warn ?? printWarning;
|
|
133
138
|
const version = options.version ?? readPackageVersion();
|
|
139
|
+
const instanceId = options.instanceId ?? randomUUID();
|
|
134
140
|
const startedAtMs = Date.now();
|
|
135
141
|
const assets = await loadStaticAssets(
|
|
136
142
|
options.publicDir ?? resolvePackageDir(join("web", "public"))
|
|
@@ -148,7 +154,7 @@ async function startWebServer(options) {
|
|
|
148
154
|
return entry;
|
|
149
155
|
};
|
|
150
156
|
const handleRequest = async (req, res) => {
|
|
151
|
-
baseHeaders(res);
|
|
157
|
+
baseHeaders(res, instanceId);
|
|
152
158
|
const requestUrl = new URL(req.url ?? "/", "http://localhost");
|
|
153
159
|
const path = requestUrl.pathname;
|
|
154
160
|
if (req.method === "POST" && path === "/api/config/agent") {
|
|
@@ -290,6 +296,9 @@ async function startWebServer(options) {
|
|
|
290
296
|
if (path === "/api/health") {
|
|
291
297
|
respondJson(res, 200, {
|
|
292
298
|
ok: true,
|
|
299
|
+
pid: process.pid,
|
|
300
|
+
instanceId,
|
|
301
|
+
startedAt: new Date(startedAtMs).toISOString(),
|
|
293
302
|
uptime: Math.round((Date.now() - startedAtMs) / 1e3),
|
|
294
303
|
version,
|
|
295
304
|
refreshSeconds: options.refreshSeconds ?? 5,
|
|
@@ -358,7 +367,7 @@ async function startWebServer(options) {
|
|
|
358
367
|
closePromise = doClose();
|
|
359
368
|
return closePromise;
|
|
360
369
|
};
|
|
361
|
-
const handle = { server, host: options.host, port, url, close };
|
|
370
|
+
const handle = { server, host: options.host, port, url, instanceId, close };
|
|
362
371
|
const onSignal = (signal) => {
|
|
363
372
|
void handle.close().finally(() => {
|
|
364
373
|
process.kill(process.pid, signal);
|
|
@@ -371,12 +380,16 @@ async function startWebServer(options) {
|
|
|
371
380
|
|
|
372
381
|
// src/web/lock.ts
|
|
373
382
|
var WEB_LOCK_FILENAME = "web.lock";
|
|
383
|
+
var WEB_RESTART_LOCK_FILENAME = "web.restart.lock";
|
|
374
384
|
var HEALTH_PROBE_TIMEOUT_MS = 1e3;
|
|
375
385
|
var LOCK_CLAIM_RETRIES = 5;
|
|
376
386
|
var LOCK_CLAIM_RETRY_DELAY_MS = 100;
|
|
377
387
|
function getWebLockFile(options = {}) {
|
|
378
388
|
return join2(getGlobalRoot(options), WEB_LOCK_FILENAME);
|
|
379
389
|
}
|
|
390
|
+
function getWebRestartLockFile(options = {}) {
|
|
391
|
+
return join2(getGlobalRoot(options), WEB_RESTART_LOCK_FILENAME);
|
|
392
|
+
}
|
|
380
393
|
function sleep(ms) {
|
|
381
394
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
382
395
|
}
|
|
@@ -389,11 +402,81 @@ async function probeHealth(host, port) {
|
|
|
389
402
|
const res = await fetch(probeUrl(host, port), {
|
|
390
403
|
signal: AbortSignal.timeout(HEALTH_PROBE_TIMEOUT_MS)
|
|
391
404
|
});
|
|
392
|
-
|
|
405
|
+
if (!res.ok) return null;
|
|
406
|
+
const body = await res.json();
|
|
407
|
+
if (body.ok !== true) return null;
|
|
408
|
+
return {
|
|
409
|
+
ok: true,
|
|
410
|
+
...typeof body.pid === "number" && Number.isInteger(body.pid) && body.pid > 0 ? { pid: body.pid } : {},
|
|
411
|
+
...typeof body.instanceId === "string" && body.instanceId !== "" ? { instanceId: body.instanceId } : {},
|
|
412
|
+
...typeof body.startedAt === "string" && body.startedAt !== "" ? { startedAt: body.startedAt } : {},
|
|
413
|
+
...typeof body.uptime === "number" && Number.isFinite(body.uptime) ? { uptime: body.uptime } : {},
|
|
414
|
+
...typeof body.version === "string" && body.version !== "" ? { version: body.version } : {}
|
|
415
|
+
};
|
|
393
416
|
} catch {
|
|
394
|
-
return
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function sameLock(a, b) {
|
|
421
|
+
return a !== null && a.pid === b.pid && a.port === b.port && a.host === b.host && a.startedAt === b.startedAt && a.instanceId === b.instanceId;
|
|
422
|
+
}
|
|
423
|
+
async function removeWebLockIfOwned(lockFile, expected) {
|
|
424
|
+
if (sameLock(await readWebLock(lockFile), expected)) {
|
|
425
|
+
await removeWebLock(lockFile);
|
|
395
426
|
}
|
|
396
427
|
}
|
|
428
|
+
async function inspectProcessCommand(pid) {
|
|
429
|
+
const invocation = process.platform === "win32" ? {
|
|
430
|
+
command: "powershell.exe",
|
|
431
|
+
args: [
|
|
432
|
+
"-NoProfile",
|
|
433
|
+
"-NonInteractive",
|
|
434
|
+
"-Command",
|
|
435
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
436
|
+
]
|
|
437
|
+
} : { command: "ps", args: ["-p", String(pid), "-o", "command="] };
|
|
438
|
+
try {
|
|
439
|
+
const result = await execa(invocation.command, invocation.args, { reject: false });
|
|
440
|
+
const command = result.stdout.trim();
|
|
441
|
+
return result.exitCode === 0 && command !== "" ? command : null;
|
|
442
|
+
} catch {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async function findListeningProcess(port) {
|
|
447
|
+
try {
|
|
448
|
+
let pids;
|
|
449
|
+
if (process.platform === "win32") {
|
|
450
|
+
const result = await execa(
|
|
451
|
+
"powershell.exe",
|
|
452
|
+
[
|
|
453
|
+
"-NoProfile",
|
|
454
|
+
"-NonInteractive",
|
|
455
|
+
"-Command",
|
|
456
|
+
`(Get-NetTCPConnection -State Listen -LocalPort ${port} -ErrorAction SilentlyContinue | Select-Object -ExpandProperty OwningProcess -Unique) -join [Environment]::NewLine`
|
|
457
|
+
],
|
|
458
|
+
{ reject: false }
|
|
459
|
+
);
|
|
460
|
+
pids = result.stdout.split(/\s+/).map(Number).filter((pid2) => Number.isInteger(pid2) && pid2 > 0);
|
|
461
|
+
} else {
|
|
462
|
+
const result = await execa("lsof", ["-nP", "-t", `-iTCP:${port}`, "-sTCP:LISTEN"], {
|
|
463
|
+
reject: false
|
|
464
|
+
});
|
|
465
|
+
pids = result.stdout.split(/\s+/).map(Number).filter((pid2) => Number.isInteger(pid2) && pid2 > 0);
|
|
466
|
+
}
|
|
467
|
+
const unique = [...new Set(pids)];
|
|
468
|
+
if (unique.length !== 1) return null;
|
|
469
|
+
const pid = unique[0];
|
|
470
|
+
const command = await inspectProcessCommand(pid);
|
|
471
|
+
return command === null ? null : { pid, command };
|
|
472
|
+
} catch {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function isIssueFlowWebServe(owner, port) {
|
|
477
|
+
const escapedPort = String(port).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
478
|
+
return /(?:^|\s)web\s+serve(?:\s|$)/.test(owner.command) && new RegExp(`(?:^|\\s)--port(?:=|\\s+)${escapedPort}(?:\\s|$)`).test(owner.command) && /issue-flow|(?:^|[/\\])dist[/\\]cli\.js/.test(owner.command);
|
|
479
|
+
}
|
|
397
480
|
async function readWebLock(lockFile) {
|
|
398
481
|
let raw;
|
|
399
482
|
try {
|
|
@@ -417,10 +500,11 @@ async function removeWebLock(lockFile) {
|
|
|
417
500
|
async function detectActiveInstance(lockFile) {
|
|
418
501
|
const lock = await readWebLock(lockFile);
|
|
419
502
|
if (lock === null) return null;
|
|
420
|
-
|
|
503
|
+
const health = isProcessAlive(lock.pid) ? await probeHealth(lock.host, lock.port) : null;
|
|
504
|
+
if (health !== null && (lock.instanceId === void 0 || health.instanceId === lock.instanceId)) {
|
|
421
505
|
return lock;
|
|
422
506
|
}
|
|
423
|
-
await
|
|
507
|
+
await removeWebLockIfOwned(lockFile, lock);
|
|
424
508
|
return null;
|
|
425
509
|
}
|
|
426
510
|
async function claimWebLock(lockFile, lock) {
|
|
@@ -432,12 +516,74 @@ async function claimWebLock(lockFile, lock) {
|
|
|
432
516
|
throw err;
|
|
433
517
|
}
|
|
434
518
|
}
|
|
519
|
+
async function readRestartLock(file) {
|
|
520
|
+
try {
|
|
521
|
+
const value = JSON.parse(await readFile2(file, "utf-8"));
|
|
522
|
+
return typeof value.pid === "number" && Number.isInteger(value.pid) && value.pid > 0 && typeof value.operationId === "string" && value.operationId !== "" && typeof value.startedAt === "string" && value.startedAt !== "" ? { pid: value.pid, operationId: value.operationId, startedAt: value.startedAt } : null;
|
|
523
|
+
} catch {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async function removeRestartLockIfOwned(file, expected) {
|
|
528
|
+
const current = await readRestartLock(file);
|
|
529
|
+
if (current?.operationId === expected.operationId) {
|
|
530
|
+
await rm(file, { force: true }).catch(() => {
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function waitForRestart(file, timeoutMs) {
|
|
535
|
+
const deadline = Date.now() + timeoutMs;
|
|
536
|
+
while (Date.now() < deadline) {
|
|
537
|
+
const lock = await readRestartLock(file);
|
|
538
|
+
if (lock === null) {
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
if (!isProcessAlive(lock.pid)) {
|
|
542
|
+
await removeRestartLockIfOwned(file, lock);
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
await sleep(LOCK_CLAIM_RETRY_DELAY_MS);
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
async function acquireRestartLock(file, timeoutMs) {
|
|
550
|
+
const deadline = Date.now() + timeoutMs;
|
|
551
|
+
let waited = false;
|
|
552
|
+
while (Date.now() < deadline) {
|
|
553
|
+
const lock = {
|
|
554
|
+
pid: process.pid,
|
|
555
|
+
operationId: randomUUID2(),
|
|
556
|
+
startedAt: isoNow()
|
|
557
|
+
};
|
|
558
|
+
try {
|
|
559
|
+
await writeFile(file, JSON.stringify(lock, null, 2), { encoding: "utf-8", flag: "wx" });
|
|
560
|
+
return {
|
|
561
|
+
lock,
|
|
562
|
+
waited,
|
|
563
|
+
release: () => removeRestartLockIfOwned(file, lock)
|
|
564
|
+
};
|
|
565
|
+
} catch (err) {
|
|
566
|
+
if (err.code !== "EEXIST") return null;
|
|
567
|
+
}
|
|
568
|
+
waited = true;
|
|
569
|
+
const existing = await readRestartLock(file);
|
|
570
|
+
if (existing === null || !isProcessAlive(existing.pid)) {
|
|
571
|
+
if (existing === null) await rm(file, { force: true }).catch(() => {
|
|
572
|
+
});
|
|
573
|
+
else await removeRestartLockIfOwned(file, existing);
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
await sleep(LOCK_CLAIM_RETRY_DELAY_MS);
|
|
577
|
+
}
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
435
580
|
function reusedHandle(lock) {
|
|
436
581
|
const displayHost = lock.host === "0.0.0.0" ? "localhost" : lock.host;
|
|
437
582
|
return {
|
|
438
583
|
host: lock.host,
|
|
439
584
|
port: lock.port,
|
|
440
585
|
url: `http://${displayHost}:${lock.port}`,
|
|
586
|
+
instanceId: lock.instanceId ?? `legacy-${lock.pid}-${lock.startedAt}`,
|
|
441
587
|
close: async () => {
|
|
442
588
|
}
|
|
443
589
|
};
|
|
@@ -463,7 +609,8 @@ async function ensureSingleWebServer(options, lockOptions = {}) {
|
|
|
463
609
|
pid: process.pid,
|
|
464
610
|
port: handle.port,
|
|
465
611
|
host: handle.host,
|
|
466
|
-
startedAt: isoNow()
|
|
612
|
+
startedAt: isoNow(),
|
|
613
|
+
instanceId: handle.instanceId
|
|
467
614
|
});
|
|
468
615
|
} catch (err) {
|
|
469
616
|
await handle.close();
|
|
@@ -486,73 +633,258 @@ async function ensureSingleWebServer(options, lockOptions = {}) {
|
|
|
486
633
|
}
|
|
487
634
|
var DETACHED_CLAIM_TIMEOUT_MS = 5e3;
|
|
488
635
|
var DETACHED_CLAIM_POLL_MS = 150;
|
|
636
|
+
function inferredStartedAt(health) {
|
|
637
|
+
if (health.startedAt !== void 0) return health.startedAt;
|
|
638
|
+
if (health.uptime !== void 0) {
|
|
639
|
+
return new Date(Date.now() - Math.max(0, health.uptime) * 1e3).toISOString();
|
|
640
|
+
}
|
|
641
|
+
return isoNow();
|
|
642
|
+
}
|
|
643
|
+
async function discoverManagedInstance(lockFile, options, monitorOptions) {
|
|
644
|
+
const existingLock = await readWebLock(lockFile);
|
|
645
|
+
if (existingLock !== null) {
|
|
646
|
+
const health2 = isProcessAlive(existingLock.pid) ? await probeHealth(existingLock.host, existingLock.port) : null;
|
|
647
|
+
if (health2 !== null && (existingLock.instanceId === void 0 || health2.instanceId === existingLock.instanceId)) {
|
|
648
|
+
return { instance: { lock: existingLock, health: health2, source: "lock" }, unownedMonitor: false };
|
|
649
|
+
}
|
|
650
|
+
await removeWebLockIfOwned(lockFile, existingLock);
|
|
651
|
+
}
|
|
652
|
+
const health = await probeHealth(options.host, options.port);
|
|
653
|
+
if (health === null) return { instance: null, unownedMonitor: false };
|
|
654
|
+
const inspect = monitorOptions.inspectProcess ?? inspectProcessCommand;
|
|
655
|
+
let owner = null;
|
|
656
|
+
if (health.pid !== void 0) {
|
|
657
|
+
const command = await inspect(health.pid);
|
|
658
|
+
if (command !== null) owner = { pid: health.pid, command };
|
|
659
|
+
}
|
|
660
|
+
owner ??= await (monitorOptions.findPortOwner ?? findListeningProcess)(options.port);
|
|
661
|
+
if (owner === null || !isIssueFlowWebServe(owner, options.port)) {
|
|
662
|
+
return { instance: null, unownedMonitor: true };
|
|
663
|
+
}
|
|
664
|
+
const recovered = {
|
|
665
|
+
pid: owner.pid,
|
|
666
|
+
port: options.port,
|
|
667
|
+
host: options.host,
|
|
668
|
+
startedAt: inferredStartedAt(health),
|
|
669
|
+
...health.instanceId === void 0 ? {} : { instanceId: health.instanceId }
|
|
670
|
+
};
|
|
671
|
+
const claimed = await claimWebLock(lockFile, recovered).catch(() => false);
|
|
672
|
+
if (!claimed) {
|
|
673
|
+
const winner = await readWebLock(lockFile);
|
|
674
|
+
if (winner !== null) {
|
|
675
|
+
const winnerHealth = await probeHealth(winner.host, winner.port);
|
|
676
|
+
if (winnerHealth !== null) {
|
|
677
|
+
return {
|
|
678
|
+
instance: { lock: winner, health: winnerHealth, source: "lock" },
|
|
679
|
+
unownedMonitor: false
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return { instance: { lock: recovered, health, source: "port" }, unownedMonitor: false };
|
|
685
|
+
}
|
|
686
|
+
async function stopManagedInstance(instance, lockFile, options, monitorOptions) {
|
|
687
|
+
const info = options.info ?? printInfo;
|
|
688
|
+
const warn = options.warn ?? printWarning;
|
|
689
|
+
const version = instance.health.version === void 0 ? "" : `, version ${instance.health.version}`;
|
|
690
|
+
info(`Web monitor: stopping previous instance (pid ${instance.lock.pid}${version}).`);
|
|
691
|
+
try {
|
|
692
|
+
(monitorOptions.kill ?? process.kill)(instance.lock.pid, "SIGTERM");
|
|
693
|
+
} catch (err) {
|
|
694
|
+
if (err.code === "ESRCH") {
|
|
695
|
+
await removeWebLockIfOwned(lockFile, instance.lock);
|
|
696
|
+
info(`Web monitor: previous pid ${instance.lock.pid} was already gone; stale lock removed.`);
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
warn(
|
|
700
|
+
`Web monitor: failed to stop pid ${instance.lock.pid} (${err instanceof Error ? err.message : String(err)}). Continuing the pipeline without restarting it.`
|
|
701
|
+
);
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
const isAlive = monitorOptions.isAlive ?? isProcessAlive;
|
|
705
|
+
const deadline = Date.now() + (monitorOptions.shutdownTimeoutMs ?? 5e3);
|
|
706
|
+
const pollMs = monitorOptions.claimPollIntervalMs ?? DETACHED_CLAIM_POLL_MS;
|
|
707
|
+
while (Date.now() < deadline) {
|
|
708
|
+
const alive = isAlive(instance.lock.pid);
|
|
709
|
+
const health = alive ? await probeHealth(instance.lock.host, instance.lock.port) : null;
|
|
710
|
+
if (!alive || health === null) {
|
|
711
|
+
await removeWebLockIfOwned(lockFile, instance.lock);
|
|
712
|
+
info(`Web monitor: previous instance stopped (pid ${instance.lock.pid}).`);
|
|
713
|
+
info(
|
|
714
|
+
"Web monitor: process-local static asset and ETag caches invalidated; no on-disk web build cache exists."
|
|
715
|
+
);
|
|
716
|
+
return true;
|
|
717
|
+
}
|
|
718
|
+
await sleep(pollMs);
|
|
719
|
+
}
|
|
720
|
+
warn(
|
|
721
|
+
`Web monitor: pid ${instance.lock.pid} did not confirm shutdown in time. Continuing the pipeline without starting a competing instance.`
|
|
722
|
+
);
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
async function stopWebMonitor(options, monitorOptions = {}) {
|
|
726
|
+
const info = options.info ?? printInfo;
|
|
727
|
+
const warn = options.warn ?? printWarning;
|
|
728
|
+
let lockFile;
|
|
729
|
+
let restartLockFile;
|
|
730
|
+
try {
|
|
731
|
+
await mkdir(getGlobalRoot(monitorOptions), { recursive: true });
|
|
732
|
+
lockFile = getWebLockFile(monitorOptions);
|
|
733
|
+
restartLockFile = getWebRestartLockFile(monitorOptions);
|
|
734
|
+
} catch (err) {
|
|
735
|
+
warn(
|
|
736
|
+
`Web monitor: global storage unavailable (${err instanceof Error ? err.message : String(err)}).`
|
|
737
|
+
);
|
|
738
|
+
return "failed";
|
|
739
|
+
}
|
|
740
|
+
const lease = await acquireRestartLock(
|
|
741
|
+
restartLockFile,
|
|
742
|
+
monitorOptions.restartLockTimeoutMs ?? 5e3
|
|
743
|
+
);
|
|
744
|
+
if (lease === null) {
|
|
745
|
+
warn("Web monitor: could not acquire the maintenance lock; no process was stopped.");
|
|
746
|
+
return "failed";
|
|
747
|
+
}
|
|
748
|
+
try {
|
|
749
|
+
const stale = await readWebLock(lockFile);
|
|
750
|
+
const discovered = await discoverManagedInstance(lockFile, options, monitorOptions);
|
|
751
|
+
if (discovered.instance === null) {
|
|
752
|
+
if (stale !== null && !isProcessAlive(stale.pid)) {
|
|
753
|
+
info(`Web monitor: stale lock for dead pid ${stale.pid} removed.`);
|
|
754
|
+
}
|
|
755
|
+
if (discovered.unownedMonitor) {
|
|
756
|
+
warn(
|
|
757
|
+
`Web monitor: port ${options.port} looks like an Issue Flow monitor, but its process ownership could not be verified; it was left untouched.`
|
|
758
|
+
);
|
|
759
|
+
return "unowned";
|
|
760
|
+
}
|
|
761
|
+
info("No web monitor is currently running.");
|
|
762
|
+
return "not-running";
|
|
763
|
+
}
|
|
764
|
+
return await stopManagedInstance(discovered.instance, lockFile, options, monitorOptions) ? "stopped" : "failed";
|
|
765
|
+
} finally {
|
|
766
|
+
await lease.release();
|
|
767
|
+
}
|
|
768
|
+
}
|
|
489
769
|
async function ensureWebMonitor(options, monitorOptions = {}) {
|
|
490
770
|
const warn = options.warn ?? printWarning;
|
|
491
771
|
const info = options.info ?? printInfo;
|
|
492
772
|
let lockFile;
|
|
773
|
+
let restartLockFile;
|
|
493
774
|
try {
|
|
775
|
+
await mkdir(getGlobalRoot(monitorOptions), { recursive: true });
|
|
494
776
|
lockFile = getWebLockFile(monitorOptions);
|
|
777
|
+
restartLockFile = getWebRestartLockFile(monitorOptions);
|
|
495
778
|
} catch (err) {
|
|
496
779
|
warn(
|
|
497
780
|
`Web monitor: global storage unavailable (${err instanceof Error ? err.message : String(err)}). Falling back to legacy in-process mode.`
|
|
498
781
|
);
|
|
499
782
|
return startWebServer(options);
|
|
500
783
|
}
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (entryScript === void 0) {
|
|
509
|
-
warn("Web monitor: could not determine the CLI entry point to spawn a detached server.");
|
|
784
|
+
const coordinationTimeout = monitorOptions.restartLockTimeoutMs ?? 5e3;
|
|
785
|
+
const restartRequestedAt = Date.now();
|
|
786
|
+
const restartLease = monitorOptions.restart ? await acquireRestartLock(restartLockFile, coordinationTimeout) : null;
|
|
787
|
+
if (monitorOptions.restart && restartLease === null) {
|
|
788
|
+
warn(
|
|
789
|
+
"Web monitor: could not acquire the restart lock. Continuing the pipeline without changing the server."
|
|
790
|
+
);
|
|
510
791
|
return null;
|
|
511
792
|
}
|
|
512
|
-
|
|
513
|
-
entryScript,
|
|
514
|
-
"web",
|
|
515
|
-
"serve",
|
|
516
|
-
"--port",
|
|
517
|
-
String(options.port),
|
|
518
|
-
"--host",
|
|
519
|
-
options.host
|
|
520
|
-
];
|
|
521
|
-
if (options.refreshSeconds !== void 0) {
|
|
522
|
-
args.push("--refresh", String(options.refreshSeconds));
|
|
523
|
-
}
|
|
524
|
-
const spawnFn = monitorOptions.spawn ?? nodeSpawn;
|
|
525
|
-
try {
|
|
526
|
-
const child = spawnFn(execPath, args, { detached: true, stdio: "ignore" });
|
|
527
|
-
child.unref();
|
|
528
|
-
} catch (err) {
|
|
793
|
+
if (!monitorOptions.restart && !await waitForRestart(restartLockFile, coordinationTimeout)) {
|
|
529
794
|
warn(
|
|
530
|
-
|
|
795
|
+
"Web monitor: another restart is still in progress. Continuing the pipeline without starting a competing instance."
|
|
531
796
|
);
|
|
532
797
|
return null;
|
|
533
798
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
return reusedHandle(
|
|
799
|
+
try {
|
|
800
|
+
const discovered = await discoverManagedInstance(lockFile, options, monitorOptions);
|
|
801
|
+
const active = discovered.instance;
|
|
802
|
+
if (monitorOptions.restart && restartLease?.waited === true && active !== null && Date.parse(active.lock.startedAt) >= restartRequestedAt) {
|
|
803
|
+
info(
|
|
804
|
+
`Web monitor: a concurrent restart already started pid ${active.lock.pid}; reusing ${reusedHandle(active.lock).url}`
|
|
805
|
+
);
|
|
806
|
+
return reusedHandle(active.lock);
|
|
542
807
|
}
|
|
543
|
-
|
|
808
|
+
if (active !== null && !monitorOptions.restart) {
|
|
809
|
+
if (active.source === "port") {
|
|
810
|
+
info(
|
|
811
|
+
`Recovered orphaned web monitor pid ${active.lock.pid} on port ${active.lock.port} and restored web.lock.`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
info(`Reusing existing web monitor at ${reusedHandle(active.lock).url}`);
|
|
815
|
+
return reusedHandle(active.lock);
|
|
816
|
+
}
|
|
817
|
+
if (active !== null && monitorOptions.restart) {
|
|
818
|
+
if (!await stopManagedInstance(active, lockFile, options, monitorOptions)) return null;
|
|
819
|
+
} else if (discovered.unownedMonitor) {
|
|
820
|
+
warn(
|
|
821
|
+
`Web monitor: port ${options.port} answers an Issue Flow health check, but its owning process could not be verified. It was left untouched and no competing server was started.`
|
|
822
|
+
);
|
|
823
|
+
return null;
|
|
824
|
+
} else if (monitorOptions.restart) {
|
|
825
|
+
info("Web monitor: no previous managed instance was found; starting a fresh one.");
|
|
826
|
+
info("Web monitor: no on-disk web build cache exists; no files were removed.");
|
|
827
|
+
}
|
|
828
|
+
const execPath = monitorOptions.execPath ?? process.execPath;
|
|
829
|
+
const entryScript = monitorOptions.entryScript ?? process.argv[1];
|
|
830
|
+
if (entryScript === void 0) {
|
|
831
|
+
warn("Web monitor: could not determine the CLI entry point to spawn a detached server.");
|
|
832
|
+
return null;
|
|
833
|
+
}
|
|
834
|
+
const args = [
|
|
835
|
+
entryScript,
|
|
836
|
+
"web",
|
|
837
|
+
"serve",
|
|
838
|
+
"--port",
|
|
839
|
+
String(options.port),
|
|
840
|
+
"--host",
|
|
841
|
+
options.host
|
|
842
|
+
];
|
|
843
|
+
if (options.refreshSeconds !== void 0) {
|
|
844
|
+
args.push("--refresh", String(options.refreshSeconds));
|
|
845
|
+
}
|
|
846
|
+
const spawnFn = monitorOptions.spawn ?? nodeSpawn;
|
|
847
|
+
try {
|
|
848
|
+
const child = spawnFn(execPath, args, { detached: true, stdio: "ignore" });
|
|
849
|
+
child.unref();
|
|
850
|
+
} catch (err) {
|
|
851
|
+
warn(
|
|
852
|
+
`Web monitor: failed to start the detached server (${err instanceof Error ? err.message : String(err)}). Continuing without the web server.`
|
|
853
|
+
);
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const timeoutMs = monitorOptions.claimTimeoutMs ?? DETACHED_CLAIM_TIMEOUT_MS;
|
|
857
|
+
const pollMs = monitorOptions.claimPollIntervalMs ?? DETACHED_CLAIM_POLL_MS;
|
|
858
|
+
const deadline = Date.now() + timeoutMs;
|
|
859
|
+
while (Date.now() < deadline) {
|
|
860
|
+
const claimed = await detectActiveInstance(lockFile);
|
|
861
|
+
if (claimed !== null) {
|
|
862
|
+
const handle = reusedHandle(claimed);
|
|
863
|
+
const health = await probeHealth(claimed.host, claimed.port);
|
|
864
|
+
if (monitorOptions.restart) {
|
|
865
|
+
const version = health?.version === void 0 ? "" : `, version ${health.version}`;
|
|
866
|
+
info(
|
|
867
|
+
`Web monitor: new instance started (pid ${claimed.pid}${version}) at ${chalk2.bold.cyan(handle.url)}`
|
|
868
|
+
);
|
|
869
|
+
} else {
|
|
870
|
+
info(`Web monitor running at ${chalk2.bold.cyan(handle.url)}`);
|
|
871
|
+
}
|
|
872
|
+
return handle;
|
|
873
|
+
}
|
|
874
|
+
await sleep(pollMs);
|
|
875
|
+
}
|
|
876
|
+
warn(
|
|
877
|
+
"Web monitor: the detached server did not start in time. Continuing without the web server."
|
|
878
|
+
);
|
|
879
|
+
return null;
|
|
880
|
+
} finally {
|
|
881
|
+
await restartLease?.release();
|
|
544
882
|
}
|
|
545
|
-
warn(
|
|
546
|
-
"Web monitor: the detached server did not start in time. Continuing without the web server."
|
|
547
|
-
);
|
|
548
|
-
return null;
|
|
549
883
|
}
|
|
550
884
|
|
|
551
885
|
export {
|
|
552
|
-
getWebLockFile,
|
|
553
|
-
readWebLock,
|
|
554
|
-
removeWebLock,
|
|
555
886
|
ensureSingleWebServer,
|
|
887
|
+
stopWebMonitor,
|
|
556
888
|
ensureWebMonitor
|
|
557
889
|
};
|
|
558
|
-
//# sourceMappingURL=chunk-
|
|
890
|
+
//# sourceMappingURL=chunk-BMHGRYL6.js.map
|