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.
- package/dist/index.js +54 -0
- 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
|
});
|