dsh-sessions-manager 3.7.3 → 3.7.5
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.en.md +7 -0
- package/README.md +7 -0
- package/lib/client.js +115 -11
- package/lib/client.js.map +2 -2
- package/lib/index.js +116 -9
- package/lib/index.js.map +3 -3
- package/package.json +1 -1
- package/src/active-session.js +87 -0
- package/src/auto-archive.js +9 -2
- package/src/client/index.jsx +132 -10
- package/src/index.js +88 -6
package/lib/index.js
CHANGED
|
@@ -960,7 +960,9 @@ function pickInactiveCandidates(items, options = {}) {
|
|
|
960
960
|
const now = Number.isFinite(options.now) ? options.now : Date.now();
|
|
961
961
|
const cutoff = now - days * DAY_MS;
|
|
962
962
|
const skipStarred = options.skipStarred !== false;
|
|
963
|
-
const
|
|
963
|
+
const activeIds = new Set(
|
|
964
|
+
Array.isArray(options.activeSessionIds) ? options.activeSessionIds.map((id) => String(id)) : options.activeSessionId != null ? [String(options.activeSessionId)] : []
|
|
965
|
+
);
|
|
964
966
|
const list = Array.isArray(items) ? items : [];
|
|
965
967
|
const out = [];
|
|
966
968
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -970,7 +972,7 @@ function pickInactiveCandidates(items, options = {}) {
|
|
|
970
972
|
if (seen.has(id)) continue;
|
|
971
973
|
if (item.archived) continue;
|
|
972
974
|
if (skipStarred && item.starred) continue;
|
|
973
|
-
if (
|
|
975
|
+
if (activeIds.has(id)) continue;
|
|
974
976
|
const updatedAt = Number(item.updatedAt);
|
|
975
977
|
if (!Number.isFinite(updatedAt) || updatedAt <= 0) continue;
|
|
976
978
|
if (updatedAt < cutoff) {
|
|
@@ -1350,6 +1352,58 @@ function createEmptyScanStore({ dir, file }) {
|
|
|
1350
1352
|
};
|
|
1351
1353
|
}
|
|
1352
1354
|
|
|
1355
|
+
// src/active-session.js
|
|
1356
|
+
var DEFAULT_ACTIVE_TTL_MS = 45 * 1e3;
|
|
1357
|
+
var MAX_ACTIVE_REPORTS = 32;
|
|
1358
|
+
function isUsableSessionId(value) {
|
|
1359
|
+
return typeof value === "string" && value.length > 0 && value.length <= 200 && !/[\\/\0]/.test(value) && value !== "." && value !== "..";
|
|
1360
|
+
}
|
|
1361
|
+
function createActiveSessionTracker(options = {}) {
|
|
1362
|
+
const ttl = Number.isFinite(options.ttlMs) ? Number(options.ttlMs) : DEFAULT_ACTIVE_TTL_MS;
|
|
1363
|
+
const now = typeof options.now === "function" ? options.now : () => Date.now();
|
|
1364
|
+
const reports = /* @__PURE__ */ new Map();
|
|
1365
|
+
function trim(at) {
|
|
1366
|
+
for (const [key, value] of reports) if (at - value.at > ttl) reports.delete(key);
|
|
1367
|
+
while (reports.size > MAX_ACTIVE_REPORTS) {
|
|
1368
|
+
const oldest = reports.keys().next().value;
|
|
1369
|
+
if (oldest === void 0) break;
|
|
1370
|
+
reports.delete(oldest);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
function report(clientId, sessionId, at = now()) {
|
|
1374
|
+
const key = typeof clientId === "string" && clientId ? clientId.slice(0, 64) : "default";
|
|
1375
|
+
if (sessionId === null || sessionId === void 0 || sessionId === "") {
|
|
1376
|
+
reports.delete(key);
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
if (!isUsableSessionId(sessionId)) return false;
|
|
1380
|
+
reports.set(key, { id: String(sessionId), at });
|
|
1381
|
+
trim(at);
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
function activeIds(at = now()) {
|
|
1385
|
+
const out = /* @__PURE__ */ new Set();
|
|
1386
|
+
for (const [key, value] of reports) {
|
|
1387
|
+
if (at - value.at > ttl) {
|
|
1388
|
+
reports.delete(key);
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
out.add(value.id);
|
|
1392
|
+
}
|
|
1393
|
+
return out;
|
|
1394
|
+
}
|
|
1395
|
+
return {
|
|
1396
|
+
report,
|
|
1397
|
+
activeIds,
|
|
1398
|
+
get size() {
|
|
1399
|
+
return reports.size;
|
|
1400
|
+
},
|
|
1401
|
+
clear() {
|
|
1402
|
+
reports.clear();
|
|
1403
|
+
}
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1353
1407
|
// src/handle-era-paths.js
|
|
1354
1408
|
import { readdir as readdir2, stat as stat2 } from "node:fs/promises";
|
|
1355
1409
|
import { basename, join as join10 } from "node:path";
|
|
@@ -1985,7 +2039,7 @@ function emptyScanCandidate(sizeBytes) {
|
|
|
1985
2039
|
}
|
|
1986
2040
|
|
|
1987
2041
|
// src/index.js
|
|
1988
|
-
var BUILD_STAMP = true ? "3.7.
|
|
2042
|
+
var BUILD_STAMP = true ? "3.7.5+23dc7e59" : "dev";
|
|
1989
2043
|
var name = "dsh-sessions-manager";
|
|
1990
2044
|
var inject = ["webServer", "workspaceRegistry", "sessionPersistence", "sessionQuery", "storageDomain"];
|
|
1991
2045
|
var MAX_TITLE = 80;
|
|
@@ -2052,7 +2106,7 @@ function requireSessionId(value) {
|
|
|
2052
2106
|
}
|
|
2053
2107
|
return value;
|
|
2054
2108
|
}
|
|
2055
|
-
function
|
|
2109
|
+
function legacyActiveSessionId(context) {
|
|
2056
2110
|
try {
|
|
2057
2111
|
const a = context.get("activeSession");
|
|
2058
2112
|
if (a != null) return a && a.id != null ? a.id : typeof a === "string" ? a : null;
|
|
@@ -2070,6 +2124,12 @@ function getActiveSessionId(context) {
|
|
|
2070
2124
|
}
|
|
2071
2125
|
return null;
|
|
2072
2126
|
}
|
|
2127
|
+
function getActiveSessionIds(context, tracker) {
|
|
2128
|
+
const reported = tracker ? tracker.activeIds() : null;
|
|
2129
|
+
if (reported && reported.size) return reported;
|
|
2130
|
+
const legacy = legacyActiveSessionId(context);
|
|
2131
|
+
return legacy != null ? /* @__PURE__ */ new Set([String(legacy)]) : /* @__PURE__ */ new Set();
|
|
2132
|
+
}
|
|
2073
2133
|
function apply(ctx) {
|
|
2074
2134
|
const w = ctx.workspaceRegistry;
|
|
2075
2135
|
const sp = ctx.sessionPersistence;
|
|
@@ -2081,6 +2141,7 @@ function apply(ctx) {
|
|
|
2081
2141
|
const metaCache = createSessionMetaCache();
|
|
2082
2142
|
const titleIndex = createTitleIndexStore({ dir: TRASH_DIR, file: join12(TRASH_DIR, "title-index.json") });
|
|
2083
2143
|
const emptyIndex = createEmptyScanStore({ dir: TRASH_DIR });
|
|
2144
|
+
const activeTracker = createActiveSessionTracker();
|
|
2084
2145
|
const EMPTY_VERDICT_TTL_MS = 14 * 24 * 3600 * 1e3;
|
|
2085
2146
|
async function hydrateFromPersist(ids, statsById) {
|
|
2086
2147
|
const hits = /* @__PURE__ */ new Map();
|
|
@@ -2137,6 +2198,19 @@ function apply(ctx) {
|
|
|
2137
2198
|
if (result.status === "rejected") return null;
|
|
2138
2199
|
return result;
|
|
2139
2200
|
}
|
|
2201
|
+
function projectionTitleOf(liveStore, id) {
|
|
2202
|
+
try {
|
|
2203
|
+
const reg = ctx.get("sessionProjections");
|
|
2204
|
+
if (!reg || typeof reg.cachedSnapshot !== "function") return null;
|
|
2205
|
+
const session = liveStore && liveStore.get && liveStore.get(id);
|
|
2206
|
+
if (!session) return null;
|
|
2207
|
+
const snap = reg.cachedSnapshot(session, ["title"]);
|
|
2208
|
+
const title = snap && snap.values ? snap.values.title : null;
|
|
2209
|
+
return typeof title === "string" && title.length > 0 ? title : null;
|
|
2210
|
+
} catch (e) {
|
|
2211
|
+
return null;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2140
2214
|
async function archivedState() {
|
|
2141
2215
|
const d = dom();
|
|
2142
2216
|
if (!d) throw new Error("workspace domain is not open");
|
|
@@ -2756,8 +2830,9 @@ function apply(ctx) {
|
|
|
2756
2830
|
}
|
|
2757
2831
|
async function moveOne(sid, targetPath) {
|
|
2758
2832
|
requireCapability(capabilities, "move");
|
|
2759
|
-
const
|
|
2760
|
-
|
|
2833
|
+
const activeIds = getActiveSessionIds(ctx, activeTracker);
|
|
2834
|
+
const activeId = activeIds.values().next().value != null ? activeIds.values().next().value : null;
|
|
2835
|
+
if (activeIds.has(String(sid)) || activeId != null && String(activeId) === String(sid)) {
|
|
2761
2836
|
const error = new Error("\u4F1A\u8BDD\u6B63\u88AB DSH \u6253\u5F00\uFF0C\u6682\u65F6\u65E0\u6CD5\u79FB\u52A8\uFF1B\u8BF7\u91CD\u542F DSH \u540E\u518D\u8BD5\u3002");
|
|
2762
2837
|
error.status = 409;
|
|
2763
2838
|
error.code = "DSM_SESSION_BUSY";
|
|
@@ -3225,11 +3300,29 @@ function apply(ctx) {
|
|
|
3225
3300
|
createdAt: header && header.createdAt != null ? header.createdAt : meta.createdAt
|
|
3226
3301
|
});
|
|
3227
3302
|
}
|
|
3228
|
-
const
|
|
3303
|
+
const stillMissing0 = missing.filter((id) => !persisted.has(id));
|
|
3304
|
+
const projectionMeta = /* @__PURE__ */ new Map();
|
|
3305
|
+
const stillMissing = [];
|
|
3306
|
+
for (const id of stillMissing0) {
|
|
3307
|
+
const title = projectionTitleOf(live, id);
|
|
3308
|
+
if (title == null) {
|
|
3309
|
+
stillMissing.push(id);
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
3312
|
+
const entry = entryById.get(id);
|
|
3313
|
+
const header = entry && entry.header ? entry.header : null;
|
|
3314
|
+
const meta = {
|
|
3315
|
+
title,
|
|
3316
|
+
cwd: header && typeof header.cwd === "string" && header.cwd ? header.cwd : null,
|
|
3317
|
+
createdAt: header && header.createdAt != null ? header.createdAt : null
|
|
3318
|
+
};
|
|
3319
|
+
projectionMeta.set(id, meta);
|
|
3320
|
+
metaCache.set(id, statsById.get(id), meta);
|
|
3321
|
+
}
|
|
3229
3322
|
if (stillMissing.length) enqueueWarm(stillMissing, statsById, entryById);
|
|
3230
3323
|
const items = [];
|
|
3231
3324
|
for (const id of visibleIds) {
|
|
3232
|
-
let meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || null;
|
|
3325
|
+
let meta = metaCache.get(id, statsById.get(id)) || persisted.get(id) || projectionMeta.get(id) || null;
|
|
3233
3326
|
if (!meta) {
|
|
3234
3327
|
meta = { title: null, cwd: null, createdAt: null };
|
|
3235
3328
|
const entry = entryById.get(id);
|
|
@@ -3300,7 +3393,7 @@ function apply(ctx) {
|
|
|
3300
3393
|
const candidates = pickInactiveCandidates(items, {
|
|
3301
3394
|
inactiveDays: days,
|
|
3302
3395
|
skipStarred: store.settings.skipStarred,
|
|
3303
|
-
|
|
3396
|
+
activeSessionIds: [...getActiveSessionIds(ctx, activeTracker)],
|
|
3304
3397
|
now
|
|
3305
3398
|
});
|
|
3306
3399
|
let archived = 0;
|
|
@@ -3636,6 +3729,20 @@ function apply(ctx) {
|
|
|
3636
3729
|
path: "/archived-sessions/capabilities",
|
|
3637
3730
|
handler: async (req, res) => json(res, { ...capabilities, buildStamp: BUILD_STAMP })
|
|
3638
3731
|
}));
|
|
3732
|
+
disposers.push(ctx.webServer.register({
|
|
3733
|
+
kind: "exact",
|
|
3734
|
+
path: "/archived-sessions/active-session",
|
|
3735
|
+
handler: async (req, res) => {
|
|
3736
|
+
const body = await readJsonBody(req);
|
|
3737
|
+
const clientId = body && typeof body.clientId === "string" ? body.clientId : "default";
|
|
3738
|
+
const sid = body && typeof body.sessionId === "string" && body.sessionId ? body.sessionId : null;
|
|
3739
|
+
const accepted = activeTracker.report(clientId, sid);
|
|
3740
|
+
json(res, {
|
|
3741
|
+
ok: accepted,
|
|
3742
|
+
activeSessionIds: [...activeTracker.activeIds()]
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
}));
|
|
3639
3746
|
disposers.push(ctx.webServer.register({
|
|
3640
3747
|
kind: "exact",
|
|
3641
3748
|
path: "/archived-sessions/list",
|