spectoflow 0.12.0 → 0.12.1
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 +25 -7
- package/templates/dashboard/server.js +10 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spectoflow",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "Agent-agnostic spec-driven development framework + real-time local control plane. Markdown artifacts, intent router, workflow-by-scope.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spec-driven-development",
|
|
@@ -19,12 +19,30 @@
|
|
|
19
19
|
"gemini"
|
|
20
20
|
],
|
|
21
21
|
"homepage": "https://github.com/georgesmomo/spectoflow#readme",
|
|
22
|
-
"bugs": {
|
|
23
|
-
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/georgesmomo/spectoflow/issues"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/georgesmomo/spectoflow.git"
|
|
28
|
+
},
|
|
24
29
|
"author": "Georges MOMO <georges.momo@gmail.com>",
|
|
25
30
|
"license": "MIT",
|
|
26
|
-
"bin": {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
31
|
+
"bin": {
|
|
32
|
+
"spectoflow": "bin/spectoflow.js"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin",
|
|
36
|
+
"lib",
|
|
37
|
+
"templates",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"status": "node bin/spectoflow.js status",
|
|
43
|
+
"test": "node --test"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
}
|
|
30
48
|
}
|
|
@@ -41,11 +41,19 @@ const server = http.createServer(async (req,res)=>{
|
|
|
41
41
|
if (p === '/api/agentfile' && req.method === 'GET') {
|
|
42
42
|
const rel = new URL(req.url, 'http://x').searchParams.get('path') || '';
|
|
43
43
|
const base = path.join(ROOT, '.spectoflow');
|
|
44
|
+
const aDir = path.join(base, 'agents'), sDir = path.join(base, 'skills');
|
|
44
45
|
const abs = path.resolve(base, rel);
|
|
45
|
-
const okDir = abs.startsWith(
|
|
46
|
+
const okDir = abs.startsWith(aDir + path.sep) || abs.startsWith(sDir + path.sep);
|
|
46
47
|
if (!okDir || !abs.endsWith('.md') || !fs.existsSync(abs) || fs.statSync(abs).isDirectory())
|
|
47
48
|
return sendJSON(res, 400, { error: 'not an agent/skill file' });
|
|
48
|
-
|
|
49
|
+
// Symlink guard: the resolved real path must stay within the (real) scope dirs.
|
|
50
|
+
let real; try { real = fs.realpathSync(abs); } catch { real = null; }
|
|
51
|
+
const realA = (() => { try { return fs.realpathSync(aDir); } catch { return aDir; } })();
|
|
52
|
+
const realS = (() => { try { return fs.realpathSync(sDir); } catch { return sDir; } })();
|
|
53
|
+
const okReal = real && (real.startsWith(realA + path.sep) || real.startsWith(realS + path.sep));
|
|
54
|
+
if (!okReal || !real.endsWith('.md') || fs.statSync(real).isDirectory())
|
|
55
|
+
return sendJSON(res, 400, { error: 'not an agent/skill file' });
|
|
56
|
+
return sendJSON(res, 200, { content: fs.readFileSync(real, 'utf8') });
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
if(p==='/api/events'){
|