osborn 0.9.26 → 0.9.27

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.
Files changed (2) hide show
  1. package/dist/index.js +54 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -800,6 +800,60 @@ function startApiServer(workingDir, port) {
800
800
  }
801
801
  return;
802
802
  }
803
+ // DELETE /sessions/project?slug=<slug> — remove all sessions under a slug.
804
+ // Deletes ~/.claude/projects/<slug>/ and ~/.claude/projects/osb/<slug>/
805
+ // Used by the dashboard "delete project" button.
806
+ if (req.method === 'DELETE' && url.pathname === '/sessions/project') {
807
+ if (syncToken) {
808
+ const authHeader = req.headers['authorization'] ?? '';
809
+ if (authHeader !== `Bearer ${syncToken}`) {
810
+ res.writeHead(401, { 'Content-Type': 'application/json' });
811
+ res.end(JSON.stringify({ error: 'Unauthorized' }));
812
+ return;
813
+ }
814
+ }
815
+ const slug = url.searchParams.get('slug');
816
+ if (!slug || slug.includes('..') || slug.includes('/')) {
817
+ res.writeHead(400, { 'Content-Type': 'application/json' });
818
+ res.end(JSON.stringify({ error: 'Invalid slug' }));
819
+ return;
820
+ }
821
+ const projectsDir = join(homedir(), '.claude', 'projects');
822
+ const targetDir = join(projectsDir, slug);
823
+ if (!targetDir.startsWith(projectsDir + '/')) {
824
+ res.writeHead(400, { 'Content-Type': 'application/json' });
825
+ res.end(JSON.stringify({ error: 'Invalid slug path' }));
826
+ return;
827
+ }
828
+ if (!existsSync(targetDir)) {
829
+ res.writeHead(404, { 'Content-Type': 'application/json' });
830
+ res.end(JSON.stringify({ error: 'Project not found', slug }));
831
+ return;
832
+ }
833
+ try {
834
+ // Count files before deleting so we can report what was removed
835
+ let fileCount = 0;
836
+ const countFiles = (dir) => {
837
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
838
+ if (e.isDirectory())
839
+ countFiles(join(dir, e.name));
840
+ else
841
+ fileCount++;
842
+ }
843
+ };
844
+ countFiles(targetDir);
845
+ rmSync(targetDir, { recursive: true, force: true });
846
+ console.log(`🗑️ Deleted project slug ${slug} (${fileCount} files)`);
847
+ res.writeHead(200, { 'Content-Type': 'application/json' });
848
+ res.end(JSON.stringify({ success: true, slug, filesDeleted: fileCount }));
849
+ }
850
+ catch (err) {
851
+ console.error(`❌ Failed to delete project ${slug}:`, err);
852
+ res.writeHead(500, { 'Content-Type': 'application/json' });
853
+ res.end(JSON.stringify({ error: `Delete failed: ${err.message}` }));
854
+ }
855
+ return;
856
+ }
803
857
  res.writeHead(404, { 'Content-Type': 'application/json' });
804
858
  res.end(JSON.stringify({ error: 'Not found' }));
805
859
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.26",
3
+ "version": "0.9.27",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {