session-steward 0.1.1 → 0.2.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/README.md +104 -106
- package/bin/session-steward-cli.mjs +12 -0
- package/dist/assets/index-kZ4XDVk-.js +9 -0
- package/dist/assets/index-pzaccjP4.css +2 -0
- package/dist/index.html +2 -2
- package/lib/cli.mjs +101 -5
- package/lib/providers/codex/index.mjs +4 -0
- package/lib/providers/codex/store.mjs +237 -39
- package/lib/server.mjs +181 -9
- package/package.json +12 -10
- package/dist/assets/index-DGVNvKX8.js +0 -9
- package/dist/assets/index-DTDZWQb9.css +0 -2
- package/docs/session-steward-overview.jpg +0 -0
package/lib/server.mjs
CHANGED
|
@@ -11,10 +11,12 @@ import { classifyInstalledVersion } from "./version-support.mjs";
|
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
13
|
assertDeepCleanupSupported,
|
|
14
|
+
deleteSessionDeletionBackup,
|
|
14
15
|
formatSessionForJson,
|
|
15
16
|
executeSessionDeletion,
|
|
16
17
|
fingerprintSessionDeletion,
|
|
17
18
|
diagnoseStorageCompatibility,
|
|
19
|
+
getSessionOverview,
|
|
18
20
|
getSessionRecord,
|
|
19
21
|
listSessions,
|
|
20
22
|
loadDeletionStore,
|
|
@@ -32,6 +34,9 @@ const MAX_SAVED_PLANS = 20;
|
|
|
32
34
|
const MAX_SAVED_OPERATIONS = 50;
|
|
33
35
|
const PLAN_RECORD_SAMPLE_LIMIT = 20;
|
|
34
36
|
const PLAN_REVIEW_REQUIRED = "DELETION_PLAN_REVIEW_REQUIRED";
|
|
37
|
+
const SESSION_OVERVIEW_TTL_MS = 45 * 1000;
|
|
38
|
+
const ALLOWED_INACTIVE_DAYS = new Set([30, 60, 90]);
|
|
39
|
+
const ALLOWED_ARCHIVE_STATUSES = new Set(["all", "active", "archived"]);
|
|
35
40
|
const publicDirectory = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "dist");
|
|
36
41
|
const staticAssets = new Map([
|
|
37
42
|
["/", { fileName: "index.html", contentType: "text/html; charset=utf-8" }],
|
|
@@ -164,6 +169,30 @@ function getPositiveInteger(value, fallback, maximum = Number.MAX_SAFE_INTEGER)
|
|
|
164
169
|
return Math.min(parsed, maximum);
|
|
165
170
|
}
|
|
166
171
|
|
|
172
|
+
function getInactiveBeforeMs(value) {
|
|
173
|
+
if (value === null || value === "") {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const days = Number(value);
|
|
178
|
+
|
|
179
|
+
if (!ALLOWED_INACTIVE_DAYS.has(days)) {
|
|
180
|
+
throw new Error("Last activity must be 30, 60, or 90 days.");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return Date.now() - days * 24 * 60 * 60 * 1000;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getArchiveStatus(value) {
|
|
187
|
+
const status = value || "all";
|
|
188
|
+
|
|
189
|
+
if (!ALLOWED_ARCHIVE_STATUSES.has(status)) {
|
|
190
|
+
throw new Error("Session status must be all, active, or archived.");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return status;
|
|
194
|
+
}
|
|
195
|
+
|
|
167
196
|
function getLocalRequestOrigin({ hostHeader, server }) {
|
|
168
197
|
if (typeof hostHeader !== "string") {
|
|
169
198
|
return null;
|
|
@@ -196,7 +225,15 @@ function requireMutationAuthorization({ request, requestUrl, token }) {
|
|
|
196
225
|
}
|
|
197
226
|
}
|
|
198
227
|
|
|
199
|
-
function summarizePlan(plan, preflight) {
|
|
228
|
+
function summarizePlan(plan, preflight, scope) {
|
|
229
|
+
const relatedRecordCount = plan.historyMatchCount
|
|
230
|
+
+ plan.sessionIndexMatchCount
|
|
231
|
+
+ plan.logRowCount
|
|
232
|
+
+ plan.spawnEdgeCount
|
|
233
|
+
+ (scope === "deep"
|
|
234
|
+
? plan.goalRowCount + plan.memoryRowCount + preflight.desktopStateMatchCount
|
|
235
|
+
: 0);
|
|
236
|
+
|
|
200
237
|
return {
|
|
201
238
|
availableDiskBytes: preflight.availableDiskBytes,
|
|
202
239
|
childCount: plan.childCount,
|
|
@@ -208,6 +245,7 @@ function summarizePlan(plan, preflight) {
|
|
|
208
245
|
logRowCount: plan.logRowCount,
|
|
209
246
|
memoryRowCount: plan.memoryRowCount,
|
|
210
247
|
missingTranscriptCount: plan.missingTranscriptPaths.length,
|
|
248
|
+
newestLinkedActivityAtMs: plan.newestLinkedActivityAtMs,
|
|
211
249
|
recordSamples: plan.records.slice(0, PLAN_RECORD_SAMPLE_LIMIT).map((record) => ({
|
|
212
250
|
displayName: record.displayName,
|
|
213
251
|
id: record.id,
|
|
@@ -215,7 +253,9 @@ function summarizePlan(plan, preflight) {
|
|
|
215
253
|
sessionIndexMatchCount: plan.sessionIndexMatchCount,
|
|
216
254
|
sessionCount: plan.ids.length,
|
|
217
255
|
spawnEdgeCount: plan.spawnEdgeCount,
|
|
218
|
-
|
|
256
|
+
relatedRecordCount,
|
|
257
|
+
transcriptBytes: plan.transcriptBytes,
|
|
258
|
+
transcriptCount: plan.transcriptFileCount,
|
|
219
259
|
};
|
|
220
260
|
}
|
|
221
261
|
|
|
@@ -235,9 +275,9 @@ function summarizeVerification(verification) {
|
|
|
235
275
|
|
|
236
276
|
function summarizeDeletionResult(result) {
|
|
237
277
|
return {
|
|
238
|
-
backupDirectory: result.backupDirectory,
|
|
239
278
|
deletedSessionCount: result.deletedIds.length,
|
|
240
279
|
deletedTranscriptCount: result.deletedTranscriptPaths.length,
|
|
280
|
+
recoveryBackupDeleted: false,
|
|
241
281
|
skippedTranscriptCount: result.skippedTranscriptPaths.length,
|
|
242
282
|
};
|
|
243
283
|
}
|
|
@@ -245,7 +285,9 @@ function summarizeDeletionResult(result) {
|
|
|
245
285
|
function publicOperation(operation) {
|
|
246
286
|
return {
|
|
247
287
|
backupDirectory: operation.backupDirectory ?? null,
|
|
288
|
+
backupDeleteError: operation.backupDeleteError ?? null,
|
|
248
289
|
canCancel: Boolean(operation.canCancel),
|
|
290
|
+
canDeleteBackup: Boolean(operation.canDeleteBackup),
|
|
249
291
|
canRestore: Boolean(operation.canRestore),
|
|
250
292
|
cancelRequested: Boolean(operation.cancelRequested),
|
|
251
293
|
error: operation.error ?? null,
|
|
@@ -291,6 +333,57 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
291
333
|
const deletionPlans = new Map();
|
|
292
334
|
const operations = new Map();
|
|
293
335
|
const activeTasks = new Set();
|
|
336
|
+
let overviewCache = null;
|
|
337
|
+
|
|
338
|
+
function invalidateSessionOverview() {
|
|
339
|
+
overviewCache = null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async function readSessionOverview({ refresh = false } = {}) {
|
|
343
|
+
const activeCodexHome = settings.getHome("codex");
|
|
344
|
+
|
|
345
|
+
if (
|
|
346
|
+
!refresh
|
|
347
|
+
&& overviewCache?.codexHome === activeCodexHome
|
|
348
|
+
&& overviewCache.expiresAtMs > Date.now()
|
|
349
|
+
) {
|
|
350
|
+
return overviewCache.overview;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const overview = await getSessionOverview({ codexHome: activeCodexHome });
|
|
354
|
+
overviewCache = {
|
|
355
|
+
codexHome: activeCodexHome,
|
|
356
|
+
expiresAtMs: Date.now() + SESSION_OVERVIEW_TTL_MS,
|
|
357
|
+
overview,
|
|
358
|
+
};
|
|
359
|
+
return overview;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function removeOperationBackups(operation, additionalDirectories = []) {
|
|
363
|
+
const candidates = [...new Set([
|
|
364
|
+
...(operation.backupDirectories ?? []),
|
|
365
|
+
operation.backupDirectory,
|
|
366
|
+
...additionalDirectories,
|
|
367
|
+
].filter(Boolean))]
|
|
368
|
+
.sort((left, right) => right.length - left.length);
|
|
369
|
+
const remaining = [];
|
|
370
|
+
|
|
371
|
+
for (const backupDirectory of candidates) {
|
|
372
|
+
try {
|
|
373
|
+
await deleteSessionDeletionBackup({
|
|
374
|
+
backupDirectory,
|
|
375
|
+
codexHome: operation.codexHome,
|
|
376
|
+
});
|
|
377
|
+
} catch {
|
|
378
|
+
remaining.push(backupDirectory);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
operation.backupDirectories = remaining;
|
|
383
|
+
operation.backupDirectory = remaining[0] ?? null;
|
|
384
|
+
operation.canDeleteBackup = remaining.length > 0;
|
|
385
|
+
return remaining.length === 0;
|
|
386
|
+
}
|
|
294
387
|
|
|
295
388
|
function getDeletionPlan(planId) {
|
|
296
389
|
const savedPlan = typeof planId === "string" ? deletionPlans.get(planId) : null;
|
|
@@ -380,6 +473,7 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
380
473
|
store: currentStore,
|
|
381
474
|
});
|
|
382
475
|
operation.backupDirectory = result.backupDirectory;
|
|
476
|
+
operation.backupDirectories = [result.backupDirectory];
|
|
383
477
|
operation.result = summarizeDeletionResult(result);
|
|
384
478
|
operation.canCancel = false;
|
|
385
479
|
operation.message = "Checking that cleanup completed";
|
|
@@ -395,8 +489,14 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
395
489
|
|
|
396
490
|
if (verification.complete) {
|
|
397
491
|
operation.message = "Cleanup completed";
|
|
492
|
+
if (await removeOperationBackups(operation)) {
|
|
493
|
+
operation.result.recoveryBackupDeleted = true;
|
|
494
|
+
} else {
|
|
495
|
+
operation.backupDeleteError = "Cleanup completed, but its recovery backup could not be removed.";
|
|
496
|
+
}
|
|
398
497
|
operation.status = "completed";
|
|
399
498
|
} else {
|
|
499
|
+
operation.canDeleteBackup = true;
|
|
400
500
|
operation.canRestore = true;
|
|
401
501
|
operation.error = "Cleanup finished, but some selected items remain. You can restore the recovery backup.";
|
|
402
502
|
operation.message = "Cleanup needs attention";
|
|
@@ -404,6 +504,7 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
404
504
|
}
|
|
405
505
|
} catch (error) {
|
|
406
506
|
operation.backupDirectory = error?.backupDirectory ?? null;
|
|
507
|
+
operation.backupDirectories = operation.backupDirectory ? [operation.backupDirectory] : [];
|
|
407
508
|
operation.canCancel = false;
|
|
408
509
|
operation.errorCode = error?.code ?? null;
|
|
409
510
|
operation.progress = error?.cancelled ? operation.progress : 100;
|
|
@@ -411,14 +512,19 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
411
512
|
if (error?.cancelled) {
|
|
412
513
|
operation.error = null;
|
|
413
514
|
operation.message = "Cleanup cancelled before session data changed";
|
|
515
|
+
if (!(await removeOperationBackups(operation))) {
|
|
516
|
+
operation.backupDeleteError = "Cleanup was cancelled, but its temporary backup could not be removed.";
|
|
517
|
+
}
|
|
414
518
|
operation.status = "cancelled";
|
|
415
519
|
} else {
|
|
520
|
+
operation.canDeleteBackup = Boolean(operation.backupDirectory);
|
|
416
521
|
operation.canRestore = Boolean(operation.backupDirectory);
|
|
417
522
|
operation.error = error instanceof Error ? error.message : "Cleanup could not be completed.";
|
|
418
523
|
operation.message = "Cleanup could not be completed";
|
|
419
524
|
operation.status = "failed";
|
|
420
525
|
}
|
|
421
526
|
} finally {
|
|
527
|
+
invalidateSessionOverview();
|
|
422
528
|
savedPlan.consumed = true;
|
|
423
529
|
deletionPlans.delete(savedPlan.id);
|
|
424
530
|
operation.finishedAtMs = Date.now();
|
|
@@ -431,7 +537,10 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
431
537
|
const id = randomBytes(18).toString("base64url");
|
|
432
538
|
const operation = {
|
|
433
539
|
backupDirectory: null,
|
|
540
|
+
backupDirectories: [],
|
|
541
|
+
backupDeleteError: null,
|
|
434
542
|
canCancel: true,
|
|
543
|
+
canDeleteBackup: false,
|
|
435
544
|
canRestore: false,
|
|
436
545
|
cancelRequested: false,
|
|
437
546
|
codexHome: savedPlan.codexHome,
|
|
@@ -457,7 +566,9 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
457
566
|
|
|
458
567
|
function startRestoreOperation(operation) {
|
|
459
568
|
operation.canCancel = false;
|
|
569
|
+
operation.canDeleteBackup = false;
|
|
460
570
|
operation.canRestore = false;
|
|
571
|
+
operation.backupDeleteError = null;
|
|
461
572
|
operation.error = null;
|
|
462
573
|
operation.errorCode = null;
|
|
463
574
|
operation.message = "Restore queued";
|
|
@@ -467,20 +578,38 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
467
578
|
activeOperationId = operation.id;
|
|
468
579
|
const task = (async () => {
|
|
469
580
|
try {
|
|
470
|
-
|
|
581
|
+
const restoreResult = await restoreSessionDeletionBackup({
|
|
471
582
|
backupDirectory: operation.backupDirectory,
|
|
472
583
|
codexHome: operation.codexHome,
|
|
473
584
|
onProgress: (update) => Object.assign(operation, update),
|
|
474
585
|
});
|
|
586
|
+
operation.restoreResult = restoreResult;
|
|
475
587
|
operation.message = "Recovery backup restored";
|
|
476
588
|
operation.progress = 100;
|
|
589
|
+
if (await removeOperationBackups(operation, [restoreResult.safetyBackupDirectory])) {
|
|
590
|
+
operation.restoreResult = {
|
|
591
|
+
...restoreResult,
|
|
592
|
+
recoveryBackupsDeleted: true,
|
|
593
|
+
safetyBackupDirectory: null,
|
|
594
|
+
};
|
|
595
|
+
} else {
|
|
596
|
+
operation.backupDeleteError = "The sessions were restored, but temporary recovery files could not be removed.";
|
|
597
|
+
}
|
|
477
598
|
operation.status = "restored";
|
|
478
599
|
} catch (error) {
|
|
600
|
+
if (error?.safetyBackupDirectory) {
|
|
601
|
+
operation.backupDirectories = [...new Set([
|
|
602
|
+
...(operation.backupDirectories ?? []),
|
|
603
|
+
error.safetyBackupDirectory,
|
|
604
|
+
])];
|
|
605
|
+
}
|
|
606
|
+
operation.canDeleteBackup = true;
|
|
479
607
|
operation.canRestore = true;
|
|
480
608
|
operation.error = error instanceof Error ? error.message : "The recovery backup could not be restored.";
|
|
481
609
|
operation.message = "Restore could not be completed";
|
|
482
610
|
operation.status = "restore-failed";
|
|
483
611
|
} finally {
|
|
612
|
+
invalidateSessionOverview();
|
|
484
613
|
operation.finishedAtMs = Date.now();
|
|
485
614
|
if (activeOperationId === operation.id) activeOperationId = null;
|
|
486
615
|
}
|
|
@@ -552,6 +681,7 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
552
681
|
? await settings.resetProviderHome(providerId)
|
|
553
682
|
: await settings.setProviderHome(providerId, (await readJsonBody(request)).home);
|
|
554
683
|
deletionPlans.clear();
|
|
684
|
+
invalidateSessionOverview();
|
|
555
685
|
sendJson(response, 200, { provider });
|
|
556
686
|
} finally {
|
|
557
687
|
mutationInProgress = false;
|
|
@@ -576,15 +706,28 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
576
706
|
return;
|
|
577
707
|
}
|
|
578
708
|
|
|
709
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/session-overview") {
|
|
710
|
+
const overview = await readSessionOverview({
|
|
711
|
+
refresh: requestUrl.searchParams.get("refresh") === "true",
|
|
712
|
+
});
|
|
713
|
+
sendJson(response, 200, { overview });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
|
|
579
717
|
if (request.method === "GET" && requestUrl.pathname === "/api/sessions") {
|
|
580
718
|
const result = await listSessions({
|
|
719
|
+
archiveStatus: getArchiveStatus(requestUrl.searchParams.get("archiveStatus")),
|
|
581
720
|
codexHome: settings.getHome("codex"),
|
|
721
|
+
inactiveBeforeMs: getInactiveBeforeMs(requestUrl.searchParams.get("inactiveDays")),
|
|
582
722
|
includeInternals: requestUrl.searchParams.get("includeInternals") === "true",
|
|
583
723
|
includeSupporting: requestUrl.searchParams.get("includeSupporting") === "true",
|
|
584
724
|
page: getPositiveInteger(requestUrl.searchParams.get("page"), 1),
|
|
585
725
|
pageSize: getPositiveInteger(requestUrl.searchParams.get("pageSize"), 25, 100),
|
|
586
726
|
search: requestUrl.searchParams.get("search"),
|
|
587
727
|
sort: requestUrl.searchParams.get("sort"),
|
|
728
|
+
workspace: requestUrl.searchParams.has("workspace")
|
|
729
|
+
? requestUrl.searchParams.get("workspace")
|
|
730
|
+
: undefined,
|
|
588
731
|
});
|
|
589
732
|
sendJson(response, 200, {
|
|
590
733
|
...result,
|
|
@@ -640,7 +783,7 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
640
783
|
deletionPlans.set(id, savedPlan);
|
|
641
784
|
sendJson(response, 200, {
|
|
642
785
|
plan: {
|
|
643
|
-
...summarizePlan(plan, preflight),
|
|
786
|
+
...summarizePlan(plan, preflight, scope),
|
|
644
787
|
expiresAtMs,
|
|
645
788
|
id,
|
|
646
789
|
},
|
|
@@ -674,18 +817,20 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
674
817
|
return;
|
|
675
818
|
}
|
|
676
819
|
|
|
677
|
-
const operationRoute = /^\/api\/deletions\/([^/]+)(\/restore)?$/u.exec(requestUrl.pathname);
|
|
820
|
+
const operationRoute = /^\/api\/deletions\/([^/]+)(\/restore|\/backup)?$/u.exec(requestUrl.pathname);
|
|
678
821
|
|
|
679
822
|
if (operationRoute) {
|
|
680
823
|
const operation = getOperation(decodeURIComponent(operationRoute[1]));
|
|
681
|
-
const
|
|
824
|
+
const operationAction = operationRoute[2] ?? "";
|
|
825
|
+
const restoreRoute = operationAction === "/restore";
|
|
826
|
+
const backupRoute = operationAction === "/backup";
|
|
682
827
|
|
|
683
|
-
if (request.method === "GET" && !
|
|
828
|
+
if (request.method === "GET" && !operationAction) {
|
|
684
829
|
sendJson(response, 200, { operation: publicOperation(operation) });
|
|
685
830
|
return;
|
|
686
831
|
}
|
|
687
832
|
|
|
688
|
-
if (request.method === "DELETE" && !
|
|
833
|
+
if (request.method === "DELETE" && !operationAction) {
|
|
689
834
|
requireMutationAuthorization({ request, requestUrl, token: mutationToken });
|
|
690
835
|
const cancelAccepted = operation.status === "queued" || (
|
|
691
836
|
operation.status === "running" && operation.canCancel
|
|
@@ -698,6 +843,33 @@ export async function startLocalServer({ codexHome, configDirectory, port = 0 })
|
|
|
698
843
|
return;
|
|
699
844
|
}
|
|
700
845
|
|
|
846
|
+
if (request.method === "DELETE" && backupRoute) {
|
|
847
|
+
requireMutationAuthorization({ request, requestUrl, token: mutationToken });
|
|
848
|
+
|
|
849
|
+
if (!operation.canDeleteBackup || !operation.backupDirectory) {
|
|
850
|
+
throw new Error("A recovery backup is not available for deletion.");
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
if (mutationInProgress || activeOperationId) {
|
|
854
|
+
sendJson(response, 409, { error: "Wait for the current cleanup to finish before deleting its backup." });
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
mutationInProgress = true;
|
|
859
|
+
try {
|
|
860
|
+
if (!(await removeOperationBackups(operation))) {
|
|
861
|
+
throw new Error("The recovery backup could not be deleted.");
|
|
862
|
+
}
|
|
863
|
+
operation.backupDeleteError = null;
|
|
864
|
+
operation.canRestore = false;
|
|
865
|
+
if (operation.result) operation.result.recoveryBackupDeleted = true;
|
|
866
|
+
sendJson(response, 200, { operation: publicOperation(operation) });
|
|
867
|
+
} finally {
|
|
868
|
+
mutationInProgress = false;
|
|
869
|
+
}
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
|
|
701
873
|
if (request.method === "POST" && restoreRoute) {
|
|
702
874
|
requireMutationAuthorization({ request, requestUrl, token: mutationToken });
|
|
703
875
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "session-steward",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "A local Codex session manager for safely reviewing and deleting old sessions with a browser UI or terminal CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Mallik Cheripally",
|
|
7
7
|
"type": "module",
|
|
@@ -18,23 +18,24 @@
|
|
|
18
18
|
"keywords": [
|
|
19
19
|
"codex",
|
|
20
20
|
"codex-cli",
|
|
21
|
-
"codex-sessions",
|
|
22
21
|
"openai-codex",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
22
|
+
"codex-sessions",
|
|
23
|
+
"codex-session-manager",
|
|
24
|
+
"codex-session-cleanup",
|
|
25
|
+
"codex-cleanup",
|
|
26
|
+
"codex-history",
|
|
27
|
+
"delete-codex-sessions",
|
|
25
28
|
"session-manager",
|
|
26
29
|
"session-cleanup",
|
|
30
|
+
"chatgpt-desktop",
|
|
31
|
+
"chatgpt-history",
|
|
27
32
|
"ai-coding-agent",
|
|
28
|
-
"
|
|
29
|
-
"backup",
|
|
30
|
-
"privacy",
|
|
31
|
-
"local-first"
|
|
33
|
+
"chatgpt-app-cleanup"
|
|
32
34
|
],
|
|
33
35
|
"os": ["darwin", "linux"],
|
|
34
36
|
"files": [
|
|
35
37
|
"bin",
|
|
36
38
|
"dist",
|
|
37
|
-
"docs",
|
|
38
39
|
"lib",
|
|
39
40
|
"LICENSE",
|
|
40
41
|
"README.md"
|
|
@@ -48,6 +49,7 @@
|
|
|
48
49
|
"scripts": {
|
|
49
50
|
"build": "vite build",
|
|
50
51
|
"benchmark:discovery": "node --expose-gc test/benchmarks/codex-discovery.mjs",
|
|
52
|
+
"benchmark:overview": "node --expose-gc test/benchmarks/codex-overview.mjs",
|
|
51
53
|
"benchmark:scale": "node --expose-gc test/benchmarks/codex-list.mjs",
|
|
52
54
|
"benchmark:transcripts": "node --expose-gc test/benchmarks/codex-transcripts.mjs",
|
|
53
55
|
"prepack": "npm run build",
|