dsh-sessions-manager 3.4.2 → 3.5.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/lib/index.js CHANGED
@@ -604,6 +604,101 @@ function createTitleIndexStore({ dir, file }) {
604
604
  };
605
605
  }
606
606
 
607
+ // src/compat/persistence.js
608
+ function asHeader(value) {
609
+ if (!value || typeof value !== "object") return null;
610
+ const candidate = value.header && typeof value.header === "object" ? value.header : value;
611
+ return candidate.id == null ? null : candidate;
612
+ }
613
+ function normalizePersistenceEntry(value) {
614
+ const header = asHeader(value);
615
+ if (!header) return null;
616
+ const snapshot = value && value.header === header ? value : null;
617
+ return {
618
+ header,
619
+ snapshot,
620
+ id: String(header.id),
621
+ sizeBytes: snapshot && Number.isFinite(snapshot.sizeBytes) ? Number(snapshot.sizeBytes) : null,
622
+ eventCount: snapshot && Number.isSafeInteger(snapshot.eventCount) ? snapshot.eventCount : null,
623
+ revision: snapshot ? snapshot.revision : null
624
+ };
625
+ }
626
+ function normalizePersistenceList(values) {
627
+ if (!Array.isArray(values)) return [];
628
+ return values.map(normalizePersistenceEntry).filter(Boolean);
629
+ }
630
+ function createPersistenceAdapter(service) {
631
+ if (!service || typeof service.list !== "function") throw new TypeError("sessionPersistence.list is required");
632
+ async function listEntries(options) {
633
+ return normalizePersistenceList(await service.list(options));
634
+ }
635
+ async function readSession(id, offset = 0) {
636
+ if (typeof service.readFrom === "function") {
637
+ const result = await service.readFrom(id, offset);
638
+ return {
639
+ meta: result && result.meta ? result.meta : null,
640
+ inheritedEventCount: result && Number.isSafeInteger(result.inheritedEventCount) ? result.inheritedEventCount : 0,
641
+ events: result && Array.isArray(result.events) ? result.events : []
642
+ };
643
+ }
644
+ if (typeof service.open !== "function") throw new Error("\u5F53\u524D DSH \u6301\u4E45\u5316\u670D\u52A1\u4E0D\u652F\u6301\u8BFB\u53D6\u4F1A\u8BDD");
645
+ const handle = await service.open(id, "read");
646
+ if (!handle || typeof handle.read !== "function" || typeof handle.close !== "function") {
647
+ try {
648
+ if (handle && typeof handle.close === "function") await handle.close();
649
+ } catch {
650
+ }
651
+ throw new Error("DSH \u8FD4\u56DE\u4E86\u65E0\u6548\u7684 SessionHandle");
652
+ }
653
+ try {
654
+ const events = await handle.read(offset);
655
+ return {
656
+ meta: handle.header || handle.meta || null,
657
+ inheritedEventCount: Number.isSafeInteger(handle.inheritedEventCount) ? handle.inheritedEventCount : 0,
658
+ events: Array.isArray(events) ? events : [...events || []]
659
+ };
660
+ } finally {
661
+ await handle.close();
662
+ }
663
+ }
664
+ function locate(header) {
665
+ if (typeof service.locate === "function") return service.locate(header);
666
+ return null;
667
+ }
668
+ const kind = typeof service.open === "function" ? "session-handle" : "legacy";
669
+ return { kind, listEntries, readSession, locate };
670
+ }
671
+
672
+ // src/compat/capabilities.js
673
+ function action(available, reason = null) {
674
+ return { available: !!available, reason: available ? null : reason };
675
+ }
676
+ function detectCapabilities({ persistence, workspaceRegistry }) {
677
+ const handleApi = !!(persistence && typeof persistence.open === "function");
678
+ const legacyRead = !!(persistence && typeof persistence.readFrom === "function");
679
+ const legacyLocate = !!(persistence && (typeof persistence.locate === "function" || persistence.backend && typeof persistence.backend.locate === "function"));
680
+ const workspaceInternals = !!(workspaceRegistry && workspaceRegistry.headers && workspaceRegistry.sessionPaths && typeof workspaceRegistry.replaceHeaderIndex === "function");
681
+ return {
682
+ persistence: handleApi ? "session-handle" : "legacy",
683
+ actions: {
684
+ read: action(legacyRead || handleApi, "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u53EF\u8BC6\u522B\u7684\u4F1A\u8BDD\u8BFB\u53D6\u63A5\u53E3"),
685
+ archive: action(!!(workspaceRegistry && typeof workspaceRegistry.archiveSession === "function"), "\u5F53\u524D DSH \u672A\u63D0\u4F9B\u5F52\u6863\u63A5\u53E3"),
686
+ trash: action(legacyRead || handleApi, "\u5F53\u524D DSH \u65E0\u6CD5\u8BFB\u53D6\u4F1A\u8BDD\uFF0C\u4E0D\u80FD\u5B89\u5168\u79FB\u5165\u56DE\u6536\u7AD9"),
687
+ restoreTrash: action(true),
688
+ purge: action(!handleApi && legacyLocate, "\u5F53\u524D DSH \u7248\u672C\u5C1A\u672A\u63D0\u4F9B\u7ECF\u8FC7\u9A8C\u8BC1\u7684\u5B89\u5168\u6C38\u4E45\u5220\u9664\u80FD\u529B"),
689
+ move: action(!handleApi && legacyRead && legacyLocate && workspaceInternals, "\u5F53\u524D DSH \u7248\u672C\u5C1A\u672A\u63D0\u4F9B\u7ECF\u8FC7\u9A8C\u8BC1\u7684\u8DE8\u5DE5\u4F5C\u533A\u8FC1\u79FB\u80FD\u529B")
690
+ }
691
+ };
692
+ }
693
+ function requireCapability(capabilities, name2) {
694
+ const value = capabilities && capabilities.actions && capabilities.actions[name2];
695
+ if (value && value.available) return;
696
+ const error = new Error(value && value.reason || `\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 ${name2}`);
697
+ error.status = 409;
698
+ error.code = "DSM_CAPABILITY_UNAVAILABLE";
699
+ throw error;
700
+ }
701
+
607
702
  // src/index.js
608
703
  var name = "dsh-sessions-manager";
609
704
  var inject = ["webServer", "workspaceRegistry", "sessionPersistence", "sessionQuery", "storageDomain"];
@@ -690,6 +785,8 @@ function foldTitle(events) {
690
785
  function apply(ctx) {
691
786
  const w = ctx.workspaceRegistry;
692
787
  const sp = ctx.sessionPersistence;
788
+ const persistence = createPersistenceAdapter(sp);
789
+ const capabilities = detectCapabilities({ persistence: sp, workspaceRegistry: w });
693
790
  const sq = ctx.sessionQuery;
694
791
  const dom = () => ctx.storageDomain.get("workspace");
695
792
  const authorityTitleCache = /* @__PURE__ */ new Map();
@@ -814,7 +911,7 @@ function apply(ctx) {
814
911
  }
815
912
  if (!meta.title || !meta.cwd) {
816
913
  try {
817
- const r = await sp.readFrom(id, 0);
914
+ const r = await persistence.readSession(id, 0);
818
915
  if (r.meta) {
819
916
  if (!meta.cwd) meta.cwd = r.meta.cwd || null;
820
917
  if (!meta.createdAt) meta.createdAt = r.meta.createdAt || null;
@@ -830,23 +927,25 @@ function apply(ctx) {
830
927
  async function collectUsage(preloadedHeaders) {
831
928
  const sizeById = /* @__PURE__ */ new Map();
832
929
  const mtimeById = /* @__PURE__ */ new Map();
833
- let headers = null;
834
- if (Array.isArray(preloadedHeaders)) headers = preloadedHeaders;
930
+ let entries = null;
931
+ if (Array.isArray(preloadedHeaders)) entries = preloadedHeaders;
835
932
  else {
836
933
  try {
837
- headers = await sp.list();
934
+ entries = await persistence.listEntries();
838
935
  } catch (e) {
839
- headers = [];
936
+ entries = [];
840
937
  }
841
938
  }
842
- if (!Array.isArray(headers)) headers = [];
939
+ if (!Array.isArray(entries)) entries = [];
843
940
  const CHUNK = 8;
844
- for (let i = 0; i < headers.length; i += CHUNK) {
845
- await Promise.all(headers.slice(i, i + CHUNK).map(async (header) => {
846
- const id = header && header.id != null ? String(header.id) : null;
941
+ for (let i = 0; i < entries.length; i += CHUNK) {
942
+ await Promise.all(entries.slice(i, i + CHUNK).map(async (entry) => {
943
+ const header = entry && entry.header ? entry.header : entry;
944
+ const id = entry && entry.id != null ? String(entry.id) : header && header.id != null ? String(header.id) : null;
847
945
  if (!id) return;
946
+ if (entry && Number.isFinite(entry.sizeBytes)) sizeById.set(id, Number(entry.sizeBytes));
848
947
  try {
849
- const loc = sp.locate(header);
948
+ const loc = persistence.locate(header);
850
949
  if (!loc || typeof loc.path !== "string" || !loc.path) return;
851
950
  const st = await stat(loc.path);
852
951
  if (!st) return;
@@ -918,17 +1017,20 @@ function apply(ctx) {
918
1017
  let cwd = null;
919
1018
  let title = null;
920
1019
  let removedPath = null;
1020
+ let persistenceEntry = null;
921
1021
  try {
922
- const headers = await sp.list();
923
- header = headers.find((h) => String(h.id) === sid) || null;
1022
+ const entries = await persistence.listEntries();
1023
+ const found = entries.find((entry) => entry.id === sid) || null;
1024
+ persistenceEntry = found;
1025
+ header = found ? found.header : null;
924
1026
  if (header) {
925
- const loc = sp.locate(header);
1027
+ const loc = persistence.locate(header);
926
1028
  if (loc && typeof loc.path === "string") removedPath = loc.path;
927
1029
  cwd = header.cwd || null;
928
1030
  title = header.title || header.meta && header.meta.title || null;
929
1031
  }
930
1032
  if (!title) {
931
- const r = await sp.readFrom(sid, 0);
1033
+ const r = await persistence.readSession(sid, 0);
932
1034
  if (r && r.meta) {
933
1035
  if (!cwd) cwd = r.meta.cwd;
934
1036
  title = foldTitle(r.events);
@@ -949,7 +1051,7 @@ function apply(ctx) {
949
1051
  cwd: cwd || null,
950
1052
  header: header || null,
951
1053
  originalPath: removedPath || null,
952
- sizeBytes: header && typeof header.size === "number" ? header.size : null,
1054
+ sizeBytes: persistenceEntry && Number.isFinite(persistenceEntry.sizeBytes) ? persistenceEntry.sizeBytes : null,
953
1055
  wasArchived: archived,
954
1056
  deletedAt: Date.now()
955
1057
  };
@@ -977,6 +1079,7 @@ function apply(ctx) {
977
1079
  }
978
1080
  async function purgeFromTrash(sid) {
979
1081
  requireSessionId(sid);
1082
+ requireCapability(capabilities, "purge");
980
1083
  let purged = false;
981
1084
  await mutateTrash(async (store) => {
982
1085
  const entry = store.items.find((t) => String(t.sessionId) === sid);
@@ -987,13 +1090,18 @@ function apply(ctx) {
987
1090
  }
988
1091
  let target = null;
989
1092
  try {
990
- const headers = await sp.list();
991
- const current = headers.find((h) => String(h.id) === sid);
992
- const located = current && sp.locate(current);
1093
+ const entries = await persistence.listEntries();
1094
+ const current = entries.find((entry2) => entry2.id === sid);
1095
+ const located = current && persistence.locate(current.header);
993
1096
  if (located && typeof located.path === "string") target = located.path;
994
1097
  } catch (e) {
995
1098
  }
996
1099
  if (!target && typeof entry.originalPath === "string") target = entry.originalPath;
1100
+ if (!target) {
1101
+ const error = new Error("\u65E0\u6CD5\u786E\u8BA4\u8BE5\u4F1A\u8BDD\u7684\u7269\u7406\u65E5\u5FD7\u4F4D\u7F6E\uFF0C\u5DF2\u505C\u6B62\u6C38\u4E45\u5220\u9664");
1102
+ error.status = 409;
1103
+ throw error;
1104
+ }
997
1105
  const targetOwnsSession = target && (basename(dirname2(target)) === sid || basename(target).includes(sid));
998
1106
  if (target && !targetOwnsSession) {
999
1107
  const error = new Error("\u65E5\u5FD7\u8DEF\u5F84\u4E0E\u4F1A\u8BDD ID \u4E0D\u5339\u914D\uFF0C\u5DF2\u505C\u6B62\u6C38\u4E45\u5220\u9664");
@@ -1069,6 +1177,7 @@ function apply(ctx) {
1069
1177
  return (await readTrashStore()).settings;
1070
1178
  }
1071
1179
  async function cleanupExpiredTrash() {
1180
+ if (!capabilities.actions.purge.available) return 0;
1072
1181
  const store = await readTrashStore();
1073
1182
  const days = store.settings.retentionDays;
1074
1183
  if (!days) return 0;
@@ -1102,11 +1211,12 @@ function apply(ctx) {
1102
1211
  return { canonical, entity: await w.create(canonical, basename(canonical) || "workspace") };
1103
1212
  }
1104
1213
  async function moveOne(sid, targetPath) {
1214
+ requireCapability(capabilities, "move");
1105
1215
  const activeId = getActiveSessionId(ctx);
1106
1216
  if (activeId != null && String(activeId) === String(sid)) {
1107
1217
  throw new Error("\u8BE5\u4F1A\u8BDD\u5F53\u524D\u5904\u4E8E\u6253\u5F00\u72B6\u6001\uFF0C\u8BF7\u5148\u5207\u6362\u5230\u522B\u7684\u4F1A\u8BDD\u518D\u79FB\u52A8\u3002");
1108
1218
  }
1109
- const r = await sp.readFrom(sid, 0);
1219
+ const r = await persistence.readSession(sid, 0);
1110
1220
  if (!r || !r.meta) throw new Error("\u65E0\u6CD5\u8BFB\u53D6\u8BE5\u4F1A\u8BDD\u7684\u65E5\u5FD7");
1111
1221
  const meta = r.meta;
1112
1222
  const events = r.events;
@@ -1165,7 +1275,7 @@ function apply(ctx) {
1165
1275
  return null;
1166
1276
  };
1167
1277
  if (isOpen) {
1168
- await relocateLog(meta, newHeader);
1278
+ if (!await relocateLog(meta, newHeader)) throw new Error("\u79FB\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u786E\u8BA4\u4F1A\u8BDD\u65E5\u5FD7\u5DF2\u8FC1\u79FB\u5230\u76EE\u6807\u5DE5\u4F5C\u533A");
1169
1279
  try {
1170
1280
  const st = sp.states && sp.states.get && sp.states.get(sid);
1171
1281
  if (st && st.meta) st.meta = Object.assign({}, st.meta, { cwd: canonical });
@@ -1181,7 +1291,7 @@ function apply(ctx) {
1181
1291
  oldPath = null;
1182
1292
  }
1183
1293
  if (typeof sp.create !== "function" || typeof sp.append !== "function") {
1184
- await relocateLog(meta, newHeader);
1294
+ if (!await relocateLog(meta, newHeader)) throw new Error("\u79FB\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u786E\u8BA4\u4F1A\u8BDD\u65E5\u5FD7\u5DF2\u8FC1\u79FB\u5230\u76EE\u6807\u5DE5\u4F5C\u533A");
1185
1295
  } else {
1186
1296
  const backupPath = oldPath ? `${oldPath}.move-backup-${Date.now()}` : null;
1187
1297
  if (backupPath) {
@@ -1202,7 +1312,7 @@ function apply(ctx) {
1202
1312
  try {
1203
1313
  await sp.create(newHeader);
1204
1314
  await sp.append(sid, events);
1205
- const check = await sp.readFrom(sid, 0);
1315
+ const check = await persistence.readSession(sid, 0);
1206
1316
  if (!check || !check.meta || check.meta.cwd !== canonical) {
1207
1317
  throw new Error("\u79FB\u52A8\u540E\u6821\u9A8C\u5931\u8D25\uFF1A\u4F1A\u8BDD\u5DE5\u4F5C\u76EE\u5F55\u672A\u6B63\u786E\u66F4\u65B0");
1208
1318
  }
@@ -1215,7 +1325,7 @@ function apply(ctx) {
1215
1325
  } catch (e) {
1216
1326
  if (ALREADY_EXISTS_RE.test(String(e && e.message || e))) {
1217
1327
  await restore();
1218
- await relocateLog(meta, newHeader);
1328
+ if (!await relocateLog(meta, newHeader)) throw new Error("\u79FB\u52A8\u5931\u8D25\uFF1A\u65E0\u6CD5\u786E\u8BA4\u4F1A\u8BDD\u65E5\u5FD7\u5DF2\u8FC1\u79FB\u5230\u76EE\u6807\u5DE5\u4F5C\u533A");
1219
1329
  } else {
1220
1330
  await restore();
1221
1331
  throw new Error("\u79FB\u52A8\u4F1A\u8BDD\u65E5\u5FD7\u5931\u8D25\uFF1A" + String(e && e.message || e));
@@ -1261,14 +1371,14 @@ function apply(ctx) {
1261
1371
  async function reindexRegistry() {
1262
1372
  const reg = w;
1263
1373
  if (!reg || typeof reg.replaceHeaderIndex !== "function") return false;
1264
- let headers = null;
1374
+ let entries = null;
1265
1375
  try {
1266
- headers = await sp.list();
1376
+ entries = await persistence.listEntries();
1267
1377
  } catch (e) {
1268
- headers = null;
1378
+ entries = null;
1269
1379
  }
1270
- if (!headers || !Array.isArray(headers)) return false;
1271
- await reg.replaceHeaderIndex(headers);
1380
+ if (!entries || !Array.isArray(entries)) return false;
1381
+ await reg.replaceHeaderIndex(entries.map((entry) => entry.header));
1272
1382
  if (typeof reg.rebuildEntities === "function") reg.rebuildEntities();
1273
1383
  return true;
1274
1384
  }
@@ -1304,17 +1414,17 @@ function apply(ctx) {
1304
1414
  return out;
1305
1415
  }
1306
1416
  async function allSessionItems(opts = {}) {
1307
- let headers = [];
1417
+ let entries = [];
1308
1418
  let headersOk = false;
1309
1419
  try {
1310
- headers = await sp.list();
1311
- headersOk = Array.isArray(headers);
1312
- if (!headersOk) headers = [];
1420
+ entries = await persistence.listEntries();
1421
+ headersOk = Array.isArray(entries);
1422
+ if (!headersOk) entries = [];
1313
1423
  } catch (e) {
1314
- headers = [];
1424
+ entries = [];
1315
1425
  }
1316
1426
  let live = ctx.get("sessions");
1317
- const ids = headers.map((h) => String(h.id));
1427
+ const ids = entries.map((entry) => entry.id);
1318
1428
  if (live) {
1319
1429
  try {
1320
1430
  live.list().forEach((s) => {
@@ -1339,7 +1449,7 @@ function apply(ctx) {
1339
1449
  }
1340
1450
  const currentArchived = new Set((await archivedState().catch(() => ({ archivedSessionIds: [] }))).archivedSessionIds || []);
1341
1451
  const items = [];
1342
- const usage = await collectUsage(headers);
1452
+ const usage = await collectUsage(entries);
1343
1453
  const statsById = new Map(visibleIds.map((id) => [id, { mtimeMs: usage.mtimeById.get(id), size: usage.sizeById.get(id) }]));
1344
1454
  const { cached, missing } = metaCache.partition(visibleIds, statsById);
1345
1455
  const persisted = await hydrateFromPersist(missing, statsById);
@@ -1405,14 +1515,14 @@ function apply(ctx) {
1405
1515
  }
1406
1516
  async function sidebarAuthority() {
1407
1517
  const ids = [];
1408
- let headers = [];
1518
+ let entries = [];
1409
1519
  try {
1410
- headers = await sp.list();
1520
+ entries = await persistence.listEntries();
1411
1521
  } catch (e) {
1412
- headers = [];
1522
+ entries = [];
1413
1523
  }
1414
- if (!Array.isArray(headers)) headers = [];
1415
- for (const header of headers) ids.push(String(header.id));
1524
+ if (!Array.isArray(entries)) entries = [];
1525
+ for (const entry of entries) ids.push(entry.id);
1416
1526
  const sessions = ctx.get("sessions");
1417
1527
  try {
1418
1528
  if (sessions) sessions.list().forEach((session) => {
@@ -1423,7 +1533,7 @@ function apply(ctx) {
1423
1533
  }
1424
1534
  const store = await readTrashStore();
1425
1535
  if (ids.length) {
1426
- const usage = await collectUsage(headers);
1536
+ const usage = await collectUsage(entries);
1427
1537
  const statsById = new Map(ids.map((id) => [id, { mtimeMs: usage.mtimeById.get(id), size: usage.sizeById.get(id) }]));
1428
1538
  const { cached, missing } = metaCache.partition(ids, statsById);
1429
1539
  const persisted = await hydrateFromPersist(missing, statsById);
@@ -1466,14 +1576,14 @@ function apply(ctx) {
1466
1576
  events = [];
1467
1577
  }
1468
1578
  } else {
1469
- const r = await sp.readFrom(sid, 0);
1579
+ const r = await persistence.readSession(sid, 0);
1470
1580
  if (!r || !r.meta) throw new Error("\u627E\u4E0D\u5230\u8BE5\u4F1A\u8BDD\u7684\u8BB0\u5F55\uFF08\u4F1A\u8BDD\u4E0D\u5B58\u5728\uFF09");
1471
1581
  meta = r.meta;
1472
1582
  events = Array.isArray(r.events) ? r.events : [];
1473
1583
  }
1474
1584
  let sizeBytes = null;
1475
1585
  try {
1476
- const loc = sp.locate(meta);
1586
+ const loc = persistence.locate(meta);
1477
1587
  if (loc && typeof loc.path === "string" && loc.path) {
1478
1588
  const st = await stat(loc.path);
1479
1589
  if (st && typeof st.size === "number") sizeBytes = st.size;
@@ -1558,7 +1668,8 @@ function apply(ctx) {
1558
1668
  const subagentSet = /* @__PURE__ */ new Set();
1559
1669
  try {
1560
1670
  if (typeof sp.list === "function") {
1561
- for (const h of await sp.list()) {
1671
+ for (const entry of await persistence.listEntries()) {
1672
+ const h = entry.header;
1562
1673
  if (String(h.parentSession) !== String(sid)) continue;
1563
1674
  if (h.origin === "subagent") subagentSet.add(h.id);
1564
1675
  else childrenSet.add(h.id);
@@ -1595,6 +1706,11 @@ function apply(ctx) {
1595
1706
  authorityTitleCache.set(String(session.id), event.data.title);
1596
1707
  }
1597
1708
  }));
1709
+ disposers.push(ctx.webServer.register({
1710
+ kind: "exact",
1711
+ path: "/archived-sessions/capabilities",
1712
+ handler: async (req, res) => json(res, capabilities)
1713
+ }));
1598
1714
  disposers.push(ctx.webServer.register({
1599
1715
  kind: "exact",
1600
1716
  path: "/archived-sessions/list",
@@ -1605,8 +1721,8 @@ function apply(ctx) {
1605
1721
  let materialized = /* @__PURE__ */ new Set();
1606
1722
  let live = ctx.get("sessions");
1607
1723
  try {
1608
- const headers = await sp.list();
1609
- materialized = new Set(headers.map((h) => String(h.id)));
1724
+ const entries = await persistence.listEntries();
1725
+ materialized = new Set(entries.map((entry) => entry.id));
1610
1726
  } catch (e) {
1611
1727
  }
1612
1728
  const trashStore = await readTrashStore();
@@ -1657,12 +1773,12 @@ function apply(ctx) {
1657
1773
  try {
1658
1774
  results.push({ sessionId: sid, ok: true, ...await restoreOne(sid) });
1659
1775
  } catch (e) {
1660
- results.push({ sessionId: sid, ok: false, error: String(e && e.message || e) });
1776
+ results.push({ sessionId: sid, ok: false, code: e && e.code, error: String(e && e.message || e) });
1661
1777
  }
1662
1778
  }
1663
1779
  json(res, { ok: true, restored: results.filter((r) => r.ok).length, results });
1664
1780
  } catch (e) {
1665
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
1781
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1666
1782
  }
1667
1783
  }
1668
1784
  }));
@@ -1701,7 +1817,7 @@ function apply(ctx) {
1701
1817
  }
1702
1818
  json(res, { ok: true, deleted: results.filter((r) => r.ok).length, results });
1703
1819
  } catch (e) {
1704
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
1820
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1705
1821
  }
1706
1822
  }
1707
1823
  }));
@@ -1740,11 +1856,19 @@ function apply(ctx) {
1740
1856
  try {
1741
1857
  const items = await readTrash();
1742
1858
  const results = await Promise.all(items.map(async (item) => {
1743
- let exists = false;
1744
- if (typeof item.originalPath === "string") exists = await stat(item.originalPath).then(() => true).catch(() => false);
1745
- return { sessionId: item.sessionId, status: exists ? "ok" : "missing", originalPath: item.originalPath || null };
1859
+ if (typeof item.originalPath !== "string" || !item.originalPath) {
1860
+ return { sessionId: item.sessionId, status: "unverified", originalPath: null };
1861
+ }
1862
+ const exists = await stat(item.originalPath).then(() => true).catch(() => false);
1863
+ return { sessionId: item.sessionId, status: exists ? "ok" : "missing", originalPath: item.originalPath };
1746
1864
  }));
1747
- json(res, { ok: true, healthy: results.filter((r) => r.status === "ok").length, missing: results.filter((r) => r.status === "missing").length, results });
1865
+ json(res, {
1866
+ ok: true,
1867
+ healthy: results.filter((r) => r.status === "ok").length,
1868
+ missing: results.filter((r) => r.status === "missing").length,
1869
+ unverified: results.filter((r) => r.status === "unverified").length,
1870
+ results
1871
+ });
1748
1872
  } catch (e) {
1749
1873
  json(res, { ok: false, error: String(e && e.message || e) }, errorStatus(e));
1750
1874
  }
@@ -1762,7 +1886,7 @@ function apply(ctx) {
1762
1886
  metaCache.invalidate(sid);
1763
1887
  json(res, out);
1764
1888
  } catch (e) {
1765
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
1889
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1766
1890
  }
1767
1891
  }
1768
1892
  }));
@@ -1780,7 +1904,7 @@ function apply(ctx) {
1780
1904
  });
1781
1905
  json(res, out);
1782
1906
  } catch (e) {
1783
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
1907
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1784
1908
  }
1785
1909
  }
1786
1910
  }));
@@ -1805,7 +1929,7 @@ function apply(ctx) {
1805
1929
  }
1806
1930
  json(res, { ok: true, purged: results.filter((r) => r.ok).length, results });
1807
1931
  } catch (e) {
1808
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
1932
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1809
1933
  }
1810
1934
  }
1811
1935
  }));
@@ -1847,7 +1971,7 @@ function apply(ctx) {
1847
1971
  const url = new URL(req.url, "http://localhost");
1848
1972
  const sid = url.searchParams.get("sessionId");
1849
1973
  requireSessionId(sid);
1850
- const r = await sp.readFrom(sid, 0);
1974
+ const r = await persistence.readSession(sid, 0);
1851
1975
  if (!r || !r.meta) {
1852
1976
  const error = new Error("\u65E0\u6CD5\u8BFB\u53D6\u8BE5\u4F1A\u8BDD\u7684\u65E5\u5FD7");
1853
1977
  error.status = 404;
@@ -1905,7 +2029,7 @@ function apply(ctx) {
1905
2029
  }
1906
2030
  json(res, { sessionId: sid, ...moved });
1907
2031
  } catch (e) {
1908
- json(res, { ok: false, error: String(e && e.message || e) }, 500);
2032
+ json(res, { ok: false, code: e && e.code, error: String(e && e.message || e) }, errorStatus(e));
1909
2033
  }
1910
2034
  }
1911
2035
  }));