claude-opencode-viewer 2.6.30 → 2.6.31
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/package.json +1 -1
- package/server.js +24 -15
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -693,22 +693,32 @@ const requestHandler = async (req, res) => {
|
|
|
693
693
|
return;
|
|
694
694
|
}
|
|
695
695
|
|
|
696
|
-
// API:
|
|
696
|
+
// API: 获取文档列表(扫描 /halo 或 cwd 下的 md 文件,最多两层目录)
|
|
697
697
|
if (req.url === '/api/docs') {
|
|
698
698
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
699
699
|
try {
|
|
700
|
-
const
|
|
700
|
+
const docsRoot = existsSync('/halo') ? '/halo' : process.cwd();
|
|
701
701
|
const EXCLUDE = new Set(['readme.md', 'changelog.md', 'license.md', 'claude.md', 'agents.md', 'contributing.md', 'security.md', 'context.md']);
|
|
702
|
-
const files =
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
702
|
+
const files = [];
|
|
703
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', '.opencode', '.claude', '.idea', '.vscode']);
|
|
704
|
+
const scanDir = (dir, prefix, depth) => {
|
|
705
|
+
if (depth > 5) return; // 最多 5 层,避免过深
|
|
706
|
+
try {
|
|
707
|
+
for (const f of readdirSync(dir, { withFileTypes: true })) {
|
|
708
|
+
if (f.isFile() && f.name.endsWith('.md') && !EXCLUDE.has(f.name.toLowerCase())) {
|
|
709
|
+
try {
|
|
710
|
+
const fullPath = join(dir, f.name);
|
|
711
|
+
const st = statSync(fullPath);
|
|
712
|
+
files.push({ name: join(dir, f.name), mtime: st.mtimeMs });
|
|
713
|
+
} catch {}
|
|
714
|
+
} else if (f.isDirectory() && !f.name.startsWith('.') && !SKIP_DIRS.has(f.name)) {
|
|
715
|
+
scanDir(join(dir, f.name), prefix ? prefix + '/' + f.name : f.name, depth + 1);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
} catch {}
|
|
719
|
+
};
|
|
720
|
+
scanDir(docsRoot, '', 0);
|
|
721
|
+
files.sort((a, b) => b.mtime - a.mtime);
|
|
712
722
|
res.end(JSON.stringify({ docs: files }));
|
|
713
723
|
} catch (err) {
|
|
714
724
|
res.end(JSON.stringify({ docs: [], error: err.message }));
|
|
@@ -721,13 +731,12 @@ const requestHandler = async (req, res) => {
|
|
|
721
731
|
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
722
732
|
const file = url.searchParams.get('file');
|
|
723
733
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
724
|
-
if (!file || file.includes('
|
|
734
|
+
if (!file || file.includes('..') || !file.endsWith('.md')) {
|
|
725
735
|
res.end(JSON.stringify({ error: '无效文件名' }));
|
|
726
736
|
return;
|
|
727
737
|
}
|
|
728
738
|
try {
|
|
729
|
-
const
|
|
730
|
-
const filePath = join(docsCwd, file);
|
|
739
|
+
const filePath = file;
|
|
731
740
|
if (!existsSync(filePath)) {
|
|
732
741
|
res.end(JSON.stringify({ error: '文件不存在' }));
|
|
733
742
|
return;
|