getgloss 0.8.0 → 0.8.2
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/README.md +5 -0
- package/dist/cli/index.js +504 -229
- package/dist/cli/index.js.map +1 -1
- package/dist/server/daemon.js +281 -15
- package/dist/server/daemon.js.map +1 -1
- package/dist/web/setup.md +3 -0
- package/package.json +1 -1
- package/skill/SKILL.md +52 -54
package/dist/server/daemon.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/server/daemon.ts
|
|
2
|
-
import { rm as
|
|
2
|
+
import { rm as rm3 } from "fs/promises";
|
|
3
3
|
import { serve } from "@hono/node-server";
|
|
4
4
|
|
|
5
5
|
// src/shared/paths.ts
|
|
@@ -10,7 +10,7 @@ import path from "path";
|
|
|
10
10
|
// package.json
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "getgloss",
|
|
13
|
-
version: "0.8.
|
|
13
|
+
version: "0.8.2",
|
|
14
14
|
description: "Local browser-based diff review for coding-agent loops.",
|
|
15
15
|
type: "module",
|
|
16
16
|
packageManager: "pnpm@10.33.2",
|
|
@@ -211,6 +211,9 @@ function parseJsonValue(value, guard, label) {
|
|
|
211
211
|
function isServerInfo(value) {
|
|
212
212
|
return isRecord(value) && isNumber(value.pid) && isNumber(value.port) && isString(value.version) && isString(value.startedAt) && isString(value.stateDir);
|
|
213
213
|
}
|
|
214
|
+
function isClearReviewsRequest(value) {
|
|
215
|
+
return isRecord(value) && isOptionalNonNegativeInteger(value.olderThanDays) && isOptionalBoolean(value.dryRun);
|
|
216
|
+
}
|
|
214
217
|
function isOpenFileRequest(value) {
|
|
215
218
|
return isRecord(value) && isString(value.filePath) && isOptionalString(value.turnId);
|
|
216
219
|
}
|
|
@@ -329,6 +332,12 @@ function isNumber(value) {
|
|
|
329
332
|
function isOptionalNumber(value) {
|
|
330
333
|
return value === void 0 || isNumber(value);
|
|
331
334
|
}
|
|
335
|
+
function isOptionalNonNegativeInteger(value) {
|
|
336
|
+
return value === void 0 || isNumber(value) && Number.isInteger(value) && value >= 0;
|
|
337
|
+
}
|
|
338
|
+
function isOptionalBoolean(value) {
|
|
339
|
+
return value === void 0 || isBoolean(value);
|
|
340
|
+
}
|
|
332
341
|
function isNullableNumber(value) {
|
|
333
342
|
return value === null || isNumber(value);
|
|
334
343
|
}
|
|
@@ -366,7 +375,7 @@ async function writeServerInfo(info) {
|
|
|
366
375
|
}
|
|
367
376
|
|
|
368
377
|
// src/server/index.ts
|
|
369
|
-
import { readFile as
|
|
378
|
+
import { readFile as readFile4, realpath, stat } from "fs/promises";
|
|
370
379
|
import path5 from "path";
|
|
371
380
|
import { fileURLToPath } from "url";
|
|
372
381
|
import { Hono } from "hono";
|
|
@@ -601,10 +610,224 @@ async function openLocalPath(filePath) {
|
|
|
601
610
|
|
|
602
611
|
// src/server/store.ts
|
|
603
612
|
import { createHash } from "crypto";
|
|
604
|
-
import { readdir, readFile as
|
|
613
|
+
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
605
614
|
import path4 from "path";
|
|
606
615
|
import { ulid } from "ulid";
|
|
607
616
|
|
|
617
|
+
// src/shared/cleanup.ts
|
|
618
|
+
import { readdir, readFile as readFile2, rm as rm2 } from "fs/promises";
|
|
619
|
+
var DEFAULT_REVIEW_RETENTION_DAYS = 30;
|
|
620
|
+
var clearableStatuses = /* @__PURE__ */ new Set(["submitted", "resolved", "cancelled"]);
|
|
621
|
+
var millisecondsPerDay = 24 * 60 * 60 * 1e3;
|
|
622
|
+
async function clearReviewArtifacts(options = {}) {
|
|
623
|
+
const olderThanDays = normalizeRetentionDays(options.olderThanDays);
|
|
624
|
+
const dryRun = options.dryRun === true;
|
|
625
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
626
|
+
const cutoff = new Date(now.getTime() - olderThanDays * millisecondsPerDay);
|
|
627
|
+
const reviewsDir = globalReviewsDir();
|
|
628
|
+
const candidates = [];
|
|
629
|
+
const deleted = [];
|
|
630
|
+
const skipped = [];
|
|
631
|
+
let entries;
|
|
632
|
+
try {
|
|
633
|
+
entries = await readdir(reviewsDir, { withFileTypes: true });
|
|
634
|
+
} catch (error) {
|
|
635
|
+
if (isFileNotFound(error)) {
|
|
636
|
+
return cleanupResult({
|
|
637
|
+
reviewsDir,
|
|
638
|
+
cutoff,
|
|
639
|
+
olderThanDays,
|
|
640
|
+
dryRun,
|
|
641
|
+
candidates,
|
|
642
|
+
deleted,
|
|
643
|
+
skipped
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
throw new Error(`Could not read reviews directory at ${reviewsDir}: ${formatError(error)}`, {
|
|
647
|
+
cause: error
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
for (const entry of entries) {
|
|
651
|
+
if (!entry.isDirectory()) {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const reviewId = entry.name;
|
|
655
|
+
const artifactDir = globalReviewDir(reviewId);
|
|
656
|
+
const candidate = await cleanupCandidate(reviewId, artifactDir, cutoff, skipped);
|
|
657
|
+
if (!candidate) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
candidates.push(candidate);
|
|
661
|
+
if (!dryRun) {
|
|
662
|
+
await rm2(artifactDir, { recursive: true, force: true });
|
|
663
|
+
deleted.push(candidate);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return cleanupResult({ reviewsDir, cutoff, olderThanDays, dryRun, candidates, deleted, skipped });
|
|
667
|
+
}
|
|
668
|
+
function normalizeRetentionDays(value) {
|
|
669
|
+
const days = value ?? DEFAULT_REVIEW_RETENTION_DAYS;
|
|
670
|
+
if (!Number.isInteger(days) || days < 0) {
|
|
671
|
+
throw new Error("olderThanDays must be a non-negative integer");
|
|
672
|
+
}
|
|
673
|
+
return days;
|
|
674
|
+
}
|
|
675
|
+
async function cleanupCandidate(reviewId, artifactDir, cutoff, skipped) {
|
|
676
|
+
let raw;
|
|
677
|
+
try {
|
|
678
|
+
raw = await readFile2(globalReviewMetaFile(reviewId), "utf8");
|
|
679
|
+
} catch (error) {
|
|
680
|
+
if (isFileNotFound(error)) {
|
|
681
|
+
skipped.push({ reviewId, artifactDir, reason: "missing metadata" });
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
684
|
+
skipped.push({ reviewId, artifactDir, reason: `unreadable metadata: ${formatError(error)}` });
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
let meta;
|
|
688
|
+
try {
|
|
689
|
+
meta = parseJson(raw, isStoredReviewMeta, "review metadata");
|
|
690
|
+
} catch (error) {
|
|
691
|
+
skipped.push({ reviewId, artifactDir, reason: `invalid metadata: ${formatError(error)}` });
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
if (meta.id !== reviewId) {
|
|
695
|
+
skipped.push({ reviewId, artifactDir, reason: `metadata id mismatch: ${meta.id}` });
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
if (!clearableStatuses.has(meta.status)) {
|
|
699
|
+
return null;
|
|
700
|
+
}
|
|
701
|
+
const turnState = await persistedTurnCleanupState(reviewId, artifactDir, skipped);
|
|
702
|
+
if (turnState === "preserve") {
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
const lastActivityAt = latestTimestamp([
|
|
706
|
+
...metadataTimestamps(meta),
|
|
707
|
+
...turnState === "none" ? [] : turnState.timestamps
|
|
708
|
+
]);
|
|
709
|
+
if (!lastActivityAt) {
|
|
710
|
+
skipped.push({ reviewId, artifactDir, reason: "missing valid activity timestamp" });
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
if (Date.parse(lastActivityAt) >= cutoff.getTime()) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
return {
|
|
717
|
+
reviewId,
|
|
718
|
+
status: meta.status,
|
|
719
|
+
artifactDir,
|
|
720
|
+
lastActivityAt
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
async function persistedTurnCleanupState(reviewId, artifactDir, skipped) {
|
|
724
|
+
let entries;
|
|
725
|
+
try {
|
|
726
|
+
entries = await readdir(globalReviewTurnsDir(reviewId), { withFileTypes: true });
|
|
727
|
+
} catch (error) {
|
|
728
|
+
if (isFileNotFound(error)) {
|
|
729
|
+
return "none";
|
|
730
|
+
}
|
|
731
|
+
skipped.push({
|
|
732
|
+
reviewId,
|
|
733
|
+
artifactDir,
|
|
734
|
+
reason: `unreadable turns directory: ${formatError(error)}`
|
|
735
|
+
});
|
|
736
|
+
return "preserve";
|
|
737
|
+
}
|
|
738
|
+
const turnDirs = entries.filter((entry) => entry.isDirectory());
|
|
739
|
+
if (turnDirs.length === 0) {
|
|
740
|
+
return "none";
|
|
741
|
+
}
|
|
742
|
+
const timestamps = [];
|
|
743
|
+
for (const entry of turnDirs) {
|
|
744
|
+
const turn = await readPersistedTurnMeta(reviewId, entry.name, artifactDir, skipped);
|
|
745
|
+
if (!turn) {
|
|
746
|
+
return "preserve";
|
|
747
|
+
}
|
|
748
|
+
if (turn.status === "pending" || !clearableStatuses.has(turn.status)) {
|
|
749
|
+
return "preserve";
|
|
750
|
+
}
|
|
751
|
+
timestamps.push(turn.createdAt, turn.submittedAt, turn.resolvedAt);
|
|
752
|
+
}
|
|
753
|
+
return { timestamps };
|
|
754
|
+
}
|
|
755
|
+
async function readPersistedTurnMeta(reviewId, turnDirName, artifactDir, skipped) {
|
|
756
|
+
let raw;
|
|
757
|
+
try {
|
|
758
|
+
raw = await readFile2(globalReviewTurnMetaFile(reviewId, turnDirName), "utf8");
|
|
759
|
+
} catch (error) {
|
|
760
|
+
skipped.push({
|
|
761
|
+
reviewId,
|
|
762
|
+
artifactDir,
|
|
763
|
+
reason: `${isFileNotFound(error) ? "missing" : "unreadable"} turn metadata for ${turnDirName}${isFileNotFound(error) ? "" : `: ${formatError(error)}`}`
|
|
764
|
+
});
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
const turn = parseJson(raw, isReviewTurnMeta, "review turn metadata");
|
|
769
|
+
if (turn.id !== turnDirName) {
|
|
770
|
+
skipped.push({
|
|
771
|
+
reviewId,
|
|
772
|
+
artifactDir,
|
|
773
|
+
reason: `turn metadata id mismatch for ${turnDirName}: ${turn.id}`
|
|
774
|
+
});
|
|
775
|
+
return null;
|
|
776
|
+
}
|
|
777
|
+
return turn;
|
|
778
|
+
} catch (error) {
|
|
779
|
+
skipped.push({
|
|
780
|
+
reviewId,
|
|
781
|
+
artifactDir,
|
|
782
|
+
reason: `invalid turn metadata for ${turnDirName}: ${formatError(error)}`
|
|
783
|
+
});
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function metadataTimestamps(meta) {
|
|
788
|
+
return [
|
|
789
|
+
meta.createdAt,
|
|
790
|
+
meta.submittedAt,
|
|
791
|
+
meta.resolvedAt,
|
|
792
|
+
...(meta.turns ?? []).flatMap((turn) => [
|
|
793
|
+
turn.createdAt,
|
|
794
|
+
turn.capturedAt,
|
|
795
|
+
turn.submittedAt,
|
|
796
|
+
turn.resolvedAt
|
|
797
|
+
])
|
|
798
|
+
];
|
|
799
|
+
}
|
|
800
|
+
function latestTimestamp(timestamps) {
|
|
801
|
+
const latest = Math.max(
|
|
802
|
+
...timestamps.map((timestamp) => timestamp ? Date.parse(timestamp) : Number.NaN).filter((timestamp) => Number.isFinite(timestamp))
|
|
803
|
+
);
|
|
804
|
+
return Number.isFinite(latest) ? new Date(latest).toISOString() : null;
|
|
805
|
+
}
|
|
806
|
+
function cleanupResult({
|
|
807
|
+
reviewsDir,
|
|
808
|
+
cutoff,
|
|
809
|
+
olderThanDays,
|
|
810
|
+
dryRun,
|
|
811
|
+
candidates,
|
|
812
|
+
deleted,
|
|
813
|
+
skipped
|
|
814
|
+
}) {
|
|
815
|
+
return {
|
|
816
|
+
reviewsDir,
|
|
817
|
+
cutoff: cutoff.toISOString(),
|
|
818
|
+
olderThanDays,
|
|
819
|
+
dryRun,
|
|
820
|
+
candidates,
|
|
821
|
+
deleted,
|
|
822
|
+
skipped,
|
|
823
|
+
counts: {
|
|
824
|
+
candidates: candidates.length,
|
|
825
|
+
deleted: deleted.length,
|
|
826
|
+
skipped: skipped.length
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
608
831
|
// src/shared/review-scope.ts
|
|
609
832
|
var ALL_REVIEW_SCOPE = { mode: "all" };
|
|
610
833
|
function normalizeReviewScope(diff, scope = ALL_REVIEW_SCOPE) {
|
|
@@ -789,6 +1012,15 @@ var ReviewStore = class {
|
|
|
789
1012
|
await this.loadAllReviews();
|
|
790
1013
|
return Array.from(this.reviews.values()).map((record) => record.meta).toSorted((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
791
1014
|
}
|
|
1015
|
+
async clearReviewArtifacts(options = {}) {
|
|
1016
|
+
const result = await clearReviewArtifacts(options);
|
|
1017
|
+
if (!result.dryRun) {
|
|
1018
|
+
for (const review of result.deleted) {
|
|
1019
|
+
this.reviews.delete(review.reviewId);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
return result;
|
|
1023
|
+
}
|
|
792
1024
|
async get(id) {
|
|
793
1025
|
return this.reviews.get(id) ?? await this.loadKnownReview(id);
|
|
794
1026
|
}
|
|
@@ -1017,7 +1249,7 @@ var ReviewStore = class {
|
|
|
1017
1249
|
async loadAllReviews() {
|
|
1018
1250
|
let entries;
|
|
1019
1251
|
try {
|
|
1020
|
-
entries = await
|
|
1252
|
+
entries = await readdir2(globalReviewsDir(), { withFileTypes: true });
|
|
1021
1253
|
} catch (error) {
|
|
1022
1254
|
if (isFileNotFound(error)) {
|
|
1023
1255
|
return;
|
|
@@ -1041,7 +1273,7 @@ var ReviewStore = class {
|
|
|
1041
1273
|
const metaPath = globalReviewMetaFile(id);
|
|
1042
1274
|
let metaRaw;
|
|
1043
1275
|
try {
|
|
1044
|
-
metaRaw = await
|
|
1276
|
+
metaRaw = await readFile3(metaPath, "utf8");
|
|
1045
1277
|
} catch (error) {
|
|
1046
1278
|
if (isFileNotFound(error)) {
|
|
1047
1279
|
return this.loadReviewFromTurnsOnly(id);
|
|
@@ -1095,7 +1327,7 @@ var ReviewStore = class {
|
|
|
1095
1327
|
async loadPersistedTurns(id) {
|
|
1096
1328
|
let entries;
|
|
1097
1329
|
try {
|
|
1098
|
-
entries = await
|
|
1330
|
+
entries = await readdir2(globalReviewTurnsDir(id), { withFileTypes: true });
|
|
1099
1331
|
} catch (error) {
|
|
1100
1332
|
if (isFileNotFound(error)) {
|
|
1101
1333
|
return [];
|
|
@@ -1123,8 +1355,8 @@ var ReviewStore = class {
|
|
|
1123
1355
|
let diffRaw;
|
|
1124
1356
|
try {
|
|
1125
1357
|
[metaRaw, diffRaw] = await Promise.all([
|
|
1126
|
-
|
|
1127
|
-
|
|
1358
|
+
readFile3(metaPath, "utf8"),
|
|
1359
|
+
readFile3(diffPath, "utf8")
|
|
1128
1360
|
]);
|
|
1129
1361
|
} catch (error) {
|
|
1130
1362
|
if (isFileNotFound(error)) {
|
|
@@ -1154,7 +1386,7 @@ var ReviewStore = class {
|
|
|
1154
1386
|
const diffPath = globalReviewDiffFile(id);
|
|
1155
1387
|
let diffRaw;
|
|
1156
1388
|
try {
|
|
1157
|
-
diffRaw = await
|
|
1389
|
+
diffRaw = await readFile3(diffPath, "utf8");
|
|
1158
1390
|
} catch (error) {
|
|
1159
1391
|
if (isFileNotFound(error)) {
|
|
1160
1392
|
return null;
|
|
@@ -1369,7 +1601,7 @@ function requiredPath(value, label) {
|
|
|
1369
1601
|
async function readOptionalJsonFile(filePath, guard, label) {
|
|
1370
1602
|
let raw;
|
|
1371
1603
|
try {
|
|
1372
|
-
raw = await
|
|
1604
|
+
raw = await readFile3(filePath, "utf8");
|
|
1373
1605
|
} catch (error) {
|
|
1374
1606
|
if (isFileNotFound(error)) {
|
|
1375
1607
|
return void 0;
|
|
@@ -1417,6 +1649,15 @@ function createApp(origin2, options = {}) {
|
|
|
1417
1649
|
const response = { reviews: await reviewStore.list() };
|
|
1418
1650
|
return c.json(response);
|
|
1419
1651
|
});
|
|
1652
|
+
app.post("/api/maintenance/clear-reviews", async (c) => {
|
|
1653
|
+
const parsed = await readJsonBody(c, isClearReviewsRequest, "clear reviews request");
|
|
1654
|
+
if (!parsed.ok) {
|
|
1655
|
+
return parsed.response;
|
|
1656
|
+
}
|
|
1657
|
+
const body = parsed.body;
|
|
1658
|
+
const result = await reviewStore.clearReviewArtifacts(body);
|
|
1659
|
+
return c.json(result);
|
|
1660
|
+
});
|
|
1420
1661
|
app.post("/api/reviews", async (c) => {
|
|
1421
1662
|
const parsed = await readJsonBody(c, isDiffPayload, "review diff");
|
|
1422
1663
|
if (!parsed.ok) {
|
|
@@ -1745,7 +1986,7 @@ async function serveAsset(c) {
|
|
|
1745
1986
|
const normalized = path5.normalize(requestPath).replace(/^(\.\.(\/|\\|$))+/, "");
|
|
1746
1987
|
const assetPath = path5.join(webRoot, "assets", normalized);
|
|
1747
1988
|
try {
|
|
1748
|
-
const body = await
|
|
1989
|
+
const body = await readFile4(assetPath);
|
|
1749
1990
|
return new Response(body, {
|
|
1750
1991
|
headers: {
|
|
1751
1992
|
"content-type": mimeTypes[path5.extname(assetPath)] ?? "application/octet-stream"
|
|
@@ -1760,7 +2001,7 @@ async function serveAsset(c) {
|
|
|
1760
2001
|
}
|
|
1761
2002
|
async function serveIndex() {
|
|
1762
2003
|
try {
|
|
1763
|
-
const body = await
|
|
2004
|
+
const body = await readFile4(path5.join(webRoot, "index.html"));
|
|
1764
2005
|
return new Response(body, {
|
|
1765
2006
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
1766
2007
|
});
|
|
@@ -1774,7 +2015,7 @@ async function serveIndex() {
|
|
|
1774
2015
|
function serveRootFile(fileName, contentType) {
|
|
1775
2016
|
return async () => {
|
|
1776
2017
|
try {
|
|
1777
|
-
const body = await
|
|
2018
|
+
const body = await readFile4(path5.join(webRoot, fileName));
|
|
1778
2019
|
return new Response(body, {
|
|
1779
2020
|
headers: { "content-type": contentType }
|
|
1780
2021
|
});
|
|
@@ -1826,6 +2067,30 @@ function statusForStoreError(error) {
|
|
|
1826
2067
|
return /not found/i.test(formatError(error)) ? 404 : 409;
|
|
1827
2068
|
}
|
|
1828
2069
|
|
|
2070
|
+
// src/server/maintenance.ts
|
|
2071
|
+
var defaultLogger = {
|
|
2072
|
+
info: (message) => {
|
|
2073
|
+
process.stdout.write(`${message}
|
|
2074
|
+
`);
|
|
2075
|
+
},
|
|
2076
|
+
error: (message) => {
|
|
2077
|
+
process.stderr.write(`${message}
|
|
2078
|
+
`);
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
async function runStartupCleanup(logger = defaultLogger) {
|
|
2082
|
+
try {
|
|
2083
|
+
const result = await reviewStore.clearReviewArtifacts({
|
|
2084
|
+
olderThanDays: DEFAULT_REVIEW_RETENTION_DAYS
|
|
2085
|
+
});
|
|
2086
|
+
logger.info(
|
|
2087
|
+
`Gloss cleanup deleted ${result.counts.deleted} review artifact(s); skipped ${result.counts.skipped}`
|
|
2088
|
+
);
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
logger.error(`Gloss cleanup failed: ${formatError(error)}`);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
1829
2094
|
// src/server/daemon.ts
|
|
1830
2095
|
var port = Number(process.env.GLOSS_PORT ?? "0");
|
|
1831
2096
|
var idleTimeoutMs = Number(process.env.GLOSS_IDLE_TIMEOUT_MS ?? "120000");
|
|
@@ -1857,6 +2122,7 @@ await writeServerInfo({
|
|
|
1857
2122
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1858
2123
|
stateDir: globalStateDir()
|
|
1859
2124
|
});
|
|
2125
|
+
await runStartupCleanup();
|
|
1860
2126
|
await scheduleIdleShutdown();
|
|
1861
2127
|
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
|
|
1862
2128
|
process.on(signal, () => {
|
|
@@ -1914,7 +2180,7 @@ async function shutdown(exitCode) {
|
|
|
1914
2180
|
async function removeCurrentServerInfo() {
|
|
1915
2181
|
const info = await readServerInfo().catch(() => null);
|
|
1916
2182
|
if (!info || info.pid === process.pid) {
|
|
1917
|
-
await
|
|
2183
|
+
await rm3(globalServerFile(), { force: true });
|
|
1918
2184
|
}
|
|
1919
2185
|
}
|
|
1920
2186
|
//# sourceMappingURL=daemon.js.map
|