pi-sdk-web 0.3.4 → 0.3.6

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/dist/server.js CHANGED
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import { execFileSync } from "node:child_process";
12
12
  import { existsSync, readFileSync } from "node:fs";
13
- import { readFile } from "node:fs/promises";
13
+ import { readFile, unlink } from "node:fs/promises";
14
14
  import { createServer } from "node:http";
15
15
  import { dirname, join, resolve, sep } from "node:path";
16
16
  import { fileURLToPath } from "node:url";
@@ -399,6 +399,10 @@ export class PiWebServer {
399
399
  case "get_stats":
400
400
  this.broadcastStats();
401
401
  break;
402
+ case "get_state":
403
+ // Used by the sidebar Tools refresh button
404
+ this.broadcastState();
405
+ break;
402
406
  case "bash": {
403
407
  const command = typeof data.command === "string" ? data.command : "";
404
408
  if (!command)
@@ -563,6 +567,21 @@ export class PiWebServer {
563
567
  await this.resumeSession(path);
564
568
  break;
565
569
  }
570
+ case "delete_session": {
571
+ const path = typeof data.path === "string" && data.path ? data.path : "";
572
+ if (!path)
573
+ throw new Error("Missing 'path'");
574
+ // Safety: only allow deleting known session files (never arbitrary paths)
575
+ const known = (await listSessions()).some((s) => s.path === path);
576
+ if (!known)
577
+ throw new Error("Not a known session file");
578
+ // Align with TUI: cannot delete the currently active session
579
+ if (this.session.sessionFile === path) {
580
+ throw new Error("Cannot delete the currently active session");
581
+ }
582
+ await unlink(path);
583
+ break;
584
+ }
566
585
  default:
567
586
  throw new Error(`Unsupported command: ${cmdType}`);
568
587
  }
@@ -279,6 +279,15 @@ class PiWebClient {
279
279
  const header = document.createElement('div');
280
280
  header.className = 'resources-header';
281
281
  header.textContent = 'Tools (click to expand)';
282
+ const refreshBtn = document.createElement('span');
283
+ refreshBtn.className = 'tools-refresh';
284
+ refreshBtn.title = 'Refresh tools list';
285
+ refreshBtn.textContent = '⟳';
286
+ refreshBtn.addEventListener('click', (e) => {
287
+ e.stopPropagation();
288
+ this.send({ type: 'get_state' });
289
+ });
290
+ header.appendChild(refreshBtn);
282
291
 
283
292
  const body = document.createElement('div');
284
293
  body.className = 'resources-body';
@@ -291,7 +300,7 @@ class PiWebClient {
291
300
  const expanded = div.dataset.expanded === 'true';
292
301
  div.dataset.expanded = expanded ? 'false' : 'true';
293
302
  body.style.display = expanded ? 'none' : 'block';
294
- header.textContent = expanded ? 'Tools (click to expand)' : 'Tools (click to collapse)';
303
+ header.firstChild.textContent = expanded ? 'Tools (click to expand)' : 'Tools (click to collapse)';
295
304
  });
296
305
  this.loadedResourcesEl.appendChild(div);
297
306
  }
@@ -1621,12 +1630,35 @@ class PiWebClient {
1621
1630
  this.modalList.innerHTML = '<div class="modal-message">No other sessions available</div>';
1622
1631
  return;
1623
1632
  }
1633
+ this.resumeSessions = sessions;
1624
1634
  this.renderModalItems(
1625
1635
  sessions.map((s) => ({ name: s.name || s.id, desc: s.cwd, value: s.path })),
1626
1636
  (item) => {
1627
1637
  this.send({ type: 'resume', path: item.value });
1628
1638
  },
1629
1639
  );
1640
+ // Add delete buttons to each item (stopPropagation so clicks don't resume)
1641
+ this.modalList.querySelectorAll('.modal-item').forEach((el, i) => {
1642
+ const session = sessions[i];
1643
+ el.classList.add('resume-item');
1644
+ const del = document.createElement('span');
1645
+ del.className = 'resume-delete';
1646
+ del.title = 'Delete this session';
1647
+ del.textContent = '🗑';
1648
+ del.addEventListener('click', (e) => {
1649
+ e.stopPropagation();
1650
+ this.deleteSession(session);
1651
+ });
1652
+ el.appendChild(del);
1653
+ });
1654
+ }
1655
+
1656
+ deleteSession(session) {
1657
+ const label = session.name || session.id;
1658
+ if (!window.confirm(`Delete session "${label}"?\nThis cannot be undone.`)) return;
1659
+ this.send({ type: 'delete_session', path: session.path });
1660
+ // Refresh the list after delete (errors surface via toast/error message)
1661
+ setTimeout(() => this.send({ type: 'get_sessions' }), 300);
1630
1662
  }
1631
1663
 
1632
1664
  // ------------------------------------------------------------------
@@ -171,6 +171,17 @@ body {
171
171
  color: var(--dim);
172
172
  }
173
173
 
174
+ .tools-refresh {
175
+ float: right;
176
+ cursor: pointer;
177
+ color: var(--accent);
178
+ padding: 0 4px;
179
+ }
180
+
181
+ .tools-refresh:hover {
182
+ text-decoration: underline;
183
+ }
184
+
174
185
  .resources-body {
175
186
  margin-top: 6px;
176
187
  color: var(--muted);
@@ -699,6 +710,29 @@ body {
699
710
  font-size: 13px;
700
711
  }
701
712
 
713
+ /* Resume picker items: delete icon pushed to the right, away from the path */
714
+ .modal-item.resume-item {
715
+ display: flex;
716
+ align-items: baseline;
717
+ }
718
+
719
+ .modal-item.resume-item .resume-delete {
720
+ margin-left: auto;
721
+ padding-left: 16px;
722
+ cursor: pointer;
723
+ opacity: 0.6;
724
+ }
725
+
726
+ .modal-item.resume-item .resume-delete:hover {
727
+ opacity: 1;
728
+ }
729
+
730
+ .modal-item.resume-item .modal-item-desc {
731
+ overflow: hidden;
732
+ text-overflow: ellipsis;
733
+ white-space: nowrap;
734
+ }
735
+
702
736
  .modal-item:hover {
703
737
  background: var(--accent-hover);
704
738
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Browser Web access for Pi (AI coding agent) via the Pi SDK - standalone module, zero modification to Pi itself",
5
5
  "type": "module",
6
6
  "bin": {