aisessions 1.1.2 → 1.1.4
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/bin/cli.js +47 -2
- package/package.json +3 -2
- package/src/ui/css.js +1 -1
- package/src/ui/js.js +55 -19
- package/src/ui/render.js +7 -7
package/bin/cli.js
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
import { createServer } from '../src/server.js'
|
|
3
3
|
import { parseArgs } from 'node:util'
|
|
4
4
|
import { exec } from 'node:child_process'
|
|
5
|
+
import pc from 'picocolors'
|
|
5
6
|
|
|
6
|
-
const VERSION = '1.1.
|
|
7
|
+
const VERSION = '1.1.4'
|
|
8
|
+
const DEV_HANDLE = 's41r4j'
|
|
9
|
+
const GITHUB_URL = 'https://github.com/s41r4j/aisessions'
|
|
10
|
+
const ISSUE_URL = 'https://github.com/s41r4j/aisessions/issues'
|
|
11
|
+
const X_URL = 'https://x.com/s41r4j'
|
|
7
12
|
|
|
8
13
|
const { values } = parseArgs({
|
|
9
14
|
options: {
|
|
@@ -44,9 +49,49 @@ if (values.help) {
|
|
|
44
49
|
const PORT = parseInt(values.port, 10) || 7878
|
|
45
50
|
const server = createServer()
|
|
46
51
|
|
|
52
|
+
function printBanner(url) {
|
|
53
|
+
const art = [
|
|
54
|
+
' _ _ ',
|
|
55
|
+
' __ _ (_) ___ ___ ___ ___ ___(_) ___ _ __ ___ ',
|
|
56
|
+
' / _` || |/ __|/ _ \\/ __|/ _ \\/ __| |/ _ \\| `_ \\/ __|',
|
|
57
|
+
'| (_| || |\\__ \\ __/\\__ \\ __/\\__ \\ | (_) | | | \\__ \\',
|
|
58
|
+
' \\__,_||_||___/\\___||___/\\___||___/_|\\___/|_| |_|___/',
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
console.log('')
|
|
62
|
+
art.forEach(line => console.log(pc.cyan(line)))
|
|
63
|
+
console.log('')
|
|
64
|
+
console.log(`${pc.bold(' aisessions')} ${pc.dim('v' + VERSION)} ${pc.green('ready')}`)
|
|
65
|
+
console.log(` ${pc.dim('Local UI:')} ${pc.underline(url)}`)
|
|
66
|
+
console.log(` ${pc.dim('Data store:')} ~/.aisessions/trash ~/.aisessions/backups`)
|
|
67
|
+
console.log(` ${pc.dim('Developer:')} @${DEV_HANDLE}`)
|
|
68
|
+
console.log(` ${pc.dim('GitHub:')} ${GITHUB_URL}`)
|
|
69
|
+
console.log(` ${pc.dim('X:')} ${X_URL}`)
|
|
70
|
+
console.log(` ${pc.dim('Report issue:')} ${ISSUE_URL}`)
|
|
71
|
+
console.log('')
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function startStatusLine(url) {
|
|
75
|
+
if (!process.stdout.isTTY) return
|
|
76
|
+
const frames = ['-', '\\\\', '|', '/']
|
|
77
|
+
let i = 0
|
|
78
|
+
const timer = setInterval(() => {
|
|
79
|
+
const frame = frames[i++ % frames.length]
|
|
80
|
+
process.stdout.write(`\\r ${pc.cyan(frame)} ${pc.dim('running at')} ${url} ${pc.dim('Ctrl+C to stop')} `)
|
|
81
|
+
}, 1200)
|
|
82
|
+
process.once('SIGINT', () => {
|
|
83
|
+
clearInterval(timer)
|
|
84
|
+
process.stdout.clearLine?.(0)
|
|
85
|
+
process.stdout.cursorTo?.(0)
|
|
86
|
+
console.log(pc.dim(' stopped aisessions'))
|
|
87
|
+
process.exit(0)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
|
|
47
91
|
server.listen(PORT, '127.0.0.1', () => {
|
|
48
92
|
const url = `http://127.0.0.1:${PORT}`
|
|
49
|
-
|
|
93
|
+
printBanner(url)
|
|
94
|
+
startStatusLine(url)
|
|
50
95
|
|
|
51
96
|
if (!values['no-open']) {
|
|
52
97
|
const cmd = process.platform === 'darwin' ? `open "${url}"` :
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aisessions",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Browse, manage and analyze your AI coding agent sessions — Claude Code, Codex, and more",
|
|
5
5
|
"bin": {
|
|
6
6
|
"aisessions": "bin/cli.js"
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"ccusage": "^20.0.6"
|
|
30
|
+
"ccusage": "^20.0.6",
|
|
31
|
+
"picocolors": "^1.1.1"
|
|
31
32
|
}
|
|
32
33
|
}
|
package/src/ui/css.js
CHANGED
|
@@ -373,7 +373,7 @@ html[data-theme="light"] .credit-pkg{color:rgba(24,24,20,.32)}
|
|
|
373
373
|
.ctx-banner .ctx-label{color:var(--accent);letter-spacing:1px}
|
|
374
374
|
|
|
375
375
|
/* ── USAGE PANEL ─────────────────────────────────────────────────────────────── */
|
|
376
|
-
#usage-panel{overflow-y:auto;
|
|
376
|
+
#usage-panel{overflow-y:auto;padding:0}
|
|
377
377
|
|
|
378
378
|
.stat-grid{
|
|
379
379
|
display:grid;
|
package/src/ui/js.js
CHANGED
|
@@ -76,6 +76,11 @@ document.addEventListener('click', function (e) {
|
|
|
76
76
|
if (a === 'delete-backup') { deleteBackup(meta); return; }
|
|
77
77
|
if (a === 'view-session') { viewSession(p); return; }
|
|
78
78
|
if (a === 'close-modal') { closeModal(); return; }
|
|
79
|
+
if (a === 'select-all') { selectAllSessions(); return; }
|
|
80
|
+
if (a === 'select-none') { selectNoSessions(); return; }
|
|
81
|
+
if (a === 'toggle-group') { toggleGrouping(); return; }
|
|
82
|
+
if (a === 'trash-selected') { trashSelected(); return; }
|
|
83
|
+
if (a === 'backup-selected'){ backupSelected(); return; }
|
|
79
84
|
return;
|
|
80
85
|
}
|
|
81
86
|
var row = e.target.closest('.tbl-row');
|
|
@@ -305,35 +310,60 @@ function rowHtml(s) {
|
|
|
305
310
|
}
|
|
306
311
|
|
|
307
312
|
// ── TOOLBAR ───────────────────────────────────────────────────────────────────
|
|
308
|
-
function
|
|
313
|
+
function selectAllSessions() {
|
|
314
|
+
filtered.forEach(function(s) { selected.add(s.path); });
|
|
315
|
+
renderSessions();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function selectNoSessions() {
|
|
319
|
+
selected.clear();
|
|
320
|
+
renderSessions();
|
|
321
|
+
}
|
|
309
322
|
|
|
310
|
-
|
|
311
|
-
wireBtn('btn-sel-none', function() { selected.clear(); renderSessions(); });
|
|
312
|
-
wireBtn('btn-group', function() {
|
|
323
|
+
function toggleGrouping() {
|
|
313
324
|
grouped = !grouped;
|
|
314
325
|
var el = $('btn-group');
|
|
315
326
|
if (el) { el.textContent = grouped ? 'FLAT VIEW' : 'GROUP VIEW'; el.classList.toggle('active-btn', !grouped); }
|
|
316
|
-
PAGE = 0;
|
|
317
|
-
|
|
327
|
+
PAGE = 0;
|
|
328
|
+
renderSessions();
|
|
329
|
+
}
|
|
318
330
|
|
|
319
|
-
|
|
331
|
+
async function trashSelected() {
|
|
320
332
|
if (!selected.size) { toast('SELECT SESSIONS FIRST', 'err'); return; }
|
|
321
333
|
if (!confirm('Move ' + selected.size + ' session(s) to trash?')) return;
|
|
322
334
|
var items = buildItems(Array.from(selected));
|
|
323
|
-
var res
|
|
335
|
+
var res;
|
|
336
|
+
try { res = await post('/api/trash/move', { items: items }); }
|
|
337
|
+
catch (e) { toast('TRASH FAILED: ' + e.message, 'err', 5000); return; }
|
|
338
|
+
if (!Array.isArray(res)) { toast('TRASH FAILED: INVALID SERVER RESPONSE', 'err', 5000); return; }
|
|
324
339
|
var ok = res.filter(function(r) { return r.ok; }).map(function(r) { return r.path; });
|
|
340
|
+
var fail = res.filter(function(r) { return !r.ok; });
|
|
325
341
|
ok.forEach(function(p) { ALL = ALL.filter(function(s) { return s.path !== p; }); selected.delete(p); });
|
|
326
|
-
applyFilter();
|
|
327
|
-
if (ok.length)
|
|
328
|
-
|
|
329
|
-
|
|
342
|
+
applyFilter();
|
|
343
|
+
if (ok.length) {
|
|
344
|
+
toast(ok.length + ' MOVED TO TRASH' + (fail.length ? ' / ' + fail.length + ' FAILED' : ''), fail.length ? 'err' : 'ok');
|
|
345
|
+
switchTab('trash');
|
|
346
|
+
} else {
|
|
347
|
+
toast('TRASH FAILED: ' + ((fail[0] && fail[0].error) || 'NO ITEMS MOVED'), 'err', 5000);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function backupSelected() {
|
|
330
352
|
if (!selected.size) { toast('SELECT SESSIONS FIRST', 'err'); return; }
|
|
331
353
|
var items = buildItems(Array.from(selected));
|
|
332
|
-
var res
|
|
333
|
-
|
|
334
|
-
toast(
|
|
335
|
-
if (
|
|
336
|
-
});
|
|
354
|
+
var res;
|
|
355
|
+
try { res = await post('/api/backup/create', { items: items, note: '' }); }
|
|
356
|
+
catch (e) { toast('BACKUP FAILED: ' + e.message, 'err', 5000); return; }
|
|
357
|
+
if (!Array.isArray(res)) { toast('BACKUP FAILED: INVALID SERVER RESPONSE', 'err', 5000); return; }
|
|
358
|
+
var ok = res.filter(function(r) { return r.ok; });
|
|
359
|
+
var fail = res.filter(function(r) { return !r.ok; });
|
|
360
|
+
if (ok.length) {
|
|
361
|
+
toast(ok.length + ' BACKUP(S) CREATED' + (fail.length ? ' / ' + fail.length + ' FAILED' : ''), fail.length ? 'err' : 'ok');
|
|
362
|
+
switchTab('backups');
|
|
363
|
+
} else {
|
|
364
|
+
toast('BACKUP FAILED: ' + ((fail[0] && fail[0].error) || 'NO BACKUPS CREATED'), 'err', 5000);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
337
367
|
|
|
338
368
|
// Enrich paths with session metadata for storage
|
|
339
369
|
function buildItems(paths) {
|
|
@@ -369,7 +399,10 @@ document.querySelectorAll('.tab').forEach(function(t) {
|
|
|
369
399
|
async function trashOne(path) {
|
|
370
400
|
if (!path || !confirm('Move to trash?')) return;
|
|
371
401
|
var items = buildItems([path]);
|
|
372
|
-
var res
|
|
402
|
+
var res;
|
|
403
|
+
try { res = await post('/api/trash/move', { items: items }); }
|
|
404
|
+
catch (e) { toast('ERROR: ' + e.message, 'err', 5000); return; }
|
|
405
|
+
if (!Array.isArray(res)) { toast('ERROR: INVALID SERVER RESPONSE', 'err', 5000); return; }
|
|
373
406
|
if (res[0] && res[0].ok) {
|
|
374
407
|
ALL = ALL.filter(function(s) { return s.path !== path; }); selected.delete(path);
|
|
375
408
|
applyFilter(); toast('MOVED TO TRASH', 'ok'); switchTab('trash');
|
|
@@ -378,7 +411,10 @@ async function trashOne(path) {
|
|
|
378
411
|
async function backupOne(path) {
|
|
379
412
|
if (!path) return;
|
|
380
413
|
var items = buildItems([path]);
|
|
381
|
-
var res
|
|
414
|
+
var res;
|
|
415
|
+
try { res = await post('/api/backup/create', { items: items, note: '' }); }
|
|
416
|
+
catch (e) { toast('ERROR: ' + e.message, 'err', 5000); return; }
|
|
417
|
+
if (!Array.isArray(res)) { toast('ERROR: INVALID SERVER RESPONSE', 'err', 5000); return; }
|
|
382
418
|
if (res[0] && res[0].ok) { toast('BACKUP CREATED', 'ok'); switchTab('backups'); }
|
|
383
419
|
else toast('ERROR: ' + ((res[0] && res[0].error) || '?'), 'err');
|
|
384
420
|
}
|
package/src/ui/render.js
CHANGED
|
@@ -65,7 +65,7 @@ export function renderPage() {
|
|
|
65
65
|
<span>X.com</span>
|
|
66
66
|
</a>
|
|
67
67
|
</div>
|
|
68
|
-
<div class="credit-pkg">aisessions v1.1.
|
|
68
|
+
<div class="credit-pkg">aisessions v1.1.4</div>
|
|
69
69
|
</div>
|
|
70
70
|
</nav>
|
|
71
71
|
|
|
@@ -86,14 +86,14 @@ export function renderPage() {
|
|
|
86
86
|
<!-- ── Sessions ── -->
|
|
87
87
|
<div id="sessions-panel" class="panel active">
|
|
88
88
|
<div id="toolbar">
|
|
89
|
-
<button class="btn" id="btn-sel-all">SELECT ALL</button>
|
|
90
|
-
<button class="btn" id="btn-sel-none">DESELECT</button>
|
|
91
|
-
<button class="btn" id="btn-group">FLAT VIEW</button>
|
|
89
|
+
<button class="btn" id="btn-sel-all" data-action="select-all">SELECT ALL</button>
|
|
90
|
+
<button class="btn" id="btn-sel-none" data-action="select-none">DESELECT</button>
|
|
91
|
+
<button class="btn" id="btn-group" data-action="toggle-group">FLAT VIEW</button>
|
|
92
92
|
<span class="spacer"></span>
|
|
93
93
|
<span id="sel-info">...</span>
|
|
94
94
|
<span class="spacer"></span>
|
|
95
|
-
<button class="btn success" id="btn-backup">BACKUP SEL</button>
|
|
96
|
-
<button class="btn danger" id="btn-trash">TRASH SEL</button>
|
|
95
|
+
<button class="btn success" id="btn-backup" data-action="backup-selected">BACKUP SEL</button>
|
|
96
|
+
<button class="btn danger" id="btn-trash" data-action="trash-selected">TRASH SEL</button>
|
|
97
97
|
</div>
|
|
98
98
|
<div id="tbl-wrap">
|
|
99
99
|
<div class="tbl-hdr">
|
|
@@ -125,7 +125,7 @@ export function renderPage() {
|
|
|
125
125
|
|
|
126
126
|
<!-- FOOTER -->
|
|
127
127
|
<footer id="foot">
|
|
128
|
-
<span>AISESSIONS v1.1.
|
|
128
|
+
<span>AISESSIONS v1.1.4</span>
|
|
129
129
|
<span> | </span>
|
|
130
130
|
<span id="foot-info">LOADING...</span>
|
|
131
131
|
</footer>
|