pi-sdk-web 0.4.8 → 0.4.10

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
@@ -290,6 +290,7 @@ export class PiWebServer {
290
290
  gitBranch: this.getGitBranch(this.session.sessionManager.getCwd()),
291
291
  commands: this.getCommands(),
292
292
  tools: this.getActiveTools(),
293
+ extensions: this.getLoadedExtensions(),
293
294
  };
294
295
  try {
295
296
  state.sessionStats = this.session.getSessionStats();
@@ -377,6 +378,42 @@ export class PiWebServer {
377
378
  return [];
378
379
  }
379
380
  }
381
+ /** Loaded extensions with their package names and versions (sidebar list). */
382
+ getLoadedExtensions() {
383
+ try {
384
+ const result = this.session.resourceLoader.getExtensions();
385
+ const list = [];
386
+ const seen = new Set();
387
+ for (const ext of result.extensions) {
388
+ // ext.path points at the extension entry (e.g. .../lib/node_modules/
389
+ // pi-magic-context/dist/index.js); walk up to the nearest package.json
390
+ // for the name/version.
391
+ let dir = dirname(ext.path);
392
+ while (dir !== dirname(dir)) {
393
+ const pkgFile = join(dir, "package.json");
394
+ if (existsSync(pkgFile)) {
395
+ try {
396
+ const pkg = JSON.parse(readFileSync(pkgFile, "utf8"));
397
+ const key = pkg.name ?? ext.path;
398
+ if (!seen.has(key)) {
399
+ seen.add(key);
400
+ list.push({ name: pkg.name ?? key, version: pkg.version ?? "?" });
401
+ }
402
+ }
403
+ catch {
404
+ // unreadable package.json - skip
405
+ }
406
+ break;
407
+ }
408
+ dir = dirname(dir);
409
+ }
410
+ }
411
+ return list;
412
+ }
413
+ catch {
414
+ return [];
415
+ }
416
+ }
380
417
  handleClientMessage(ws, raw) {
381
418
  let data;
382
419
  try {
@@ -397,7 +397,7 @@ class PiWebClient {
397
397
  }
398
398
 
399
399
  // Loaded resources
400
- this.renderLoadedResources(state.commands, state.tools);
400
+ this.renderLoadedResources(state.commands, state.tools, state.extensions);
401
401
 
402
402
  // First footer line: pwd (branch) • session name
403
403
  const pwd = state.cwd || '';
@@ -409,7 +409,7 @@ class PiWebClient {
409
409
  this.renderStats(state.sessionStats, state);
410
410
  }
411
411
 
412
- renderLoadedResources(commands, tools) {
412
+ renderLoadedResources(commands, tools, extensions) {
413
413
  if (!this.loadedResourcesEl) return;
414
414
  this.loadedResourcesEl.innerHTML = '';
415
415
 
@@ -443,7 +443,7 @@ class PiWebClient {
443
443
  sections.push(this.resourceSection('Prompts', groups.prompt.map((c) => c.name)));
444
444
  }
445
445
  if (groups.extension.length > 0) {
446
- sections.push(this.resourceSection('Extensions', groups.extension.map((c) => c.name)));
446
+ sections.push(this.resourceSection('Extension Cmds', groups.extension.map((c) => c.name)));
447
447
  }
448
448
  body.innerHTML = sections.join('');
449
449
 
@@ -493,6 +493,37 @@ class PiWebClient {
493
493
  });
494
494
  this.loadedResourcesEl.appendChild(div);
495
495
  }
496
+
497
+ // Separate Extensions block below Tools: loaded extension name + version
498
+ if (extensions && extensions.length > 0) {
499
+ const div = document.createElement('div');
500
+ div.className = 'resources-block';
501
+ div.dataset.expanded = 'false';
502
+ div.style.marginTop = '6px';
503
+
504
+ const header = document.createElement('div');
505
+ header.className = 'resources-header';
506
+ header.textContent = `Extensions (${extensions.length})`;
507
+
508
+ const body = document.createElement('div');
509
+ body.className = 'resources-body';
510
+ body.style.display = 'none';
511
+ body.innerHTML = extensions
512
+ .map((e) => `<div class="resources-item ext-item"><span>${this.escapeHtml(e.name)}</span><span class="ext-version">${this.escapeHtml(e.version)}</span></div>`)
513
+ .join('');
514
+
515
+ div.appendChild(header);
516
+ div.appendChild(body);
517
+ div.addEventListener('click', () => {
518
+ const expanded = div.dataset.expanded === 'true';
519
+ div.dataset.expanded = expanded ? 'false' : 'true';
520
+ body.style.display = expanded ? 'none' : 'block';
521
+ header.textContent = expanded
522
+ ? `Extensions (${extensions.length}) · click to expand`
523
+ : `Extensions (${extensions.length}) · click to collapse`;
524
+ });
525
+ this.loadedResourcesEl.appendChild(div);
526
+ }
496
527
  }
497
528
 
498
529
  resourceSection(name, items) {
@@ -213,6 +213,25 @@ body {
213
213
  text-overflow: ellipsis;
214
214
  }
215
215
 
216
+ .ext-item {
217
+ display: flex;
218
+ flex-wrap: wrap;
219
+ justify-content: space-between;
220
+ gap: 0 6px;
221
+ }
222
+
223
+ .ext-item > span:first-child {
224
+ min-width: 0;
225
+ overflow-wrap: anywhere;
226
+ }
227
+
228
+ .ext-version {
229
+ color: var(--dim);
230
+ font-size: 10px;
231
+ flex-shrink: 0;
232
+ margin-left: auto;
233
+ }
234
+
216
235
  #conn-status.connected {
217
236
  color: var(--accent);
218
237
  background: var(--accent-hover);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-sdk-web",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
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": {