@evolve.labs/devflow 0.8.3 → 0.9.0
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/lib/constants.js +1 -1
- package/lib/web.js +19 -7
- package/package.json +1 -1
- package/web/app/api/autopilot/terminal-execute/route.ts +8 -2
- package/web/lib/ptyManager.ts +25 -3
- package/web/package.json +1 -3
package/lib/constants.js
CHANGED
package/lib/web.js
CHANGED
|
@@ -49,15 +49,27 @@ async function webCommand(options) {
|
|
|
49
49
|
process.exit(1);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// 5.
|
|
52
|
+
// 5. Validate port
|
|
53
|
+
const portNum = parseInt(port, 10);
|
|
54
|
+
if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
|
|
55
|
+
console.error(`Invalid port: ${port}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 6. Start the web server
|
|
53
60
|
const isDev = options.dev || false;
|
|
54
61
|
const cmd = isDev ? 'dev' : 'start';
|
|
55
62
|
|
|
56
|
-
// Build first if not dev mode and
|
|
63
|
+
// Build first if not dev mode and no valid production build exists
|
|
57
64
|
if (!isDev) {
|
|
58
|
-
const
|
|
59
|
-
if (!fs.existsSync(
|
|
60
|
-
console.log('Building web dashboard...');
|
|
65
|
+
const buildIdPath = path.join(webDir, '.next', 'BUILD_ID');
|
|
66
|
+
if (!fs.existsSync(buildIdPath)) {
|
|
67
|
+
console.log('Building web dashboard (first run, may take a minute)...');
|
|
68
|
+
// Remove stale .next directory if it exists without BUILD_ID
|
|
69
|
+
const nextDir = path.join(webDir, '.next');
|
|
70
|
+
if (fs.existsSync(nextDir)) {
|
|
71
|
+
fs.rmSync(nextDir, { recursive: true, force: true });
|
|
72
|
+
}
|
|
61
73
|
try {
|
|
62
74
|
execSync('npx next build --webpack', {
|
|
63
75
|
cwd: webDir,
|
|
@@ -91,12 +103,12 @@ async function webCommand(options) {
|
|
|
91
103
|
// Open browser after a short delay (unless --no-open)
|
|
92
104
|
if (options.open !== false) {
|
|
93
105
|
setTimeout(() => {
|
|
94
|
-
const url = `http://localhost:${
|
|
106
|
+
const url = `http://localhost:${portNum}`;
|
|
95
107
|
try {
|
|
96
108
|
const openCmd = process.platform === 'darwin' ? 'open'
|
|
97
109
|
: process.platform === 'win32' ? 'start'
|
|
98
110
|
: 'xdg-open';
|
|
99
|
-
execSync(`${openCmd} ${url}`, { stdio: 'ignore' });
|
|
111
|
+
execSync(`${openCmd} ${JSON.stringify(url)}`, { stdio: 'ignore' });
|
|
100
112
|
} catch {
|
|
101
113
|
console.log(`Open your browser at: ${url}`);
|
|
102
114
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolve.labs/devflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Multi-agent system for software development with Claude Code. 6 specialized agents (Strategist, Architect, System Designer, Builder, Guardian, Chronicler) as slash commands.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -42,6 +42,12 @@ export async function POST(req: NextRequest) {
|
|
|
42
42
|
return NextResponse.json({ error: 'Missing required fields' }, { status: 400 });
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Sanitize sessionId to prevent path traversal
|
|
46
|
+
const safeSessionId = sessionId.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
47
|
+
if (!safeSessionId || safeSessionId !== sessionId) {
|
|
48
|
+
return NextResponse.json({ error: 'Invalid sessionId format' }, { status: 400 });
|
|
49
|
+
}
|
|
50
|
+
|
|
45
51
|
if (!isValidAgent(agent)) {
|
|
46
52
|
return NextResponse.json({ error: `Unknown agent: ${agent}` }, { status: 400 });
|
|
47
53
|
}
|
|
@@ -66,10 +72,10 @@ export async function POST(req: NextRequest) {
|
|
|
66
72
|
|
|
67
73
|
// Write prompt to temp file (avoid shell escaping issues)
|
|
68
74
|
const tmpDir = os.tmpdir();
|
|
69
|
-
const tmpFile = path.join(tmpDir, `devflow-autopilot-${
|
|
75
|
+
const tmpFile = path.join(tmpDir, `devflow-autopilot-${safeSessionId}-${agent}-${Date.now()}.md`);
|
|
70
76
|
|
|
71
77
|
try {
|
|
72
|
-
await fs.writeFile(tmpFile, fullPrompt, 'utf-8');
|
|
78
|
+
await fs.writeFile(tmpFile, fullPrompt, { encoding: 'utf-8', mode: 0o600 });
|
|
73
79
|
|
|
74
80
|
// Arm the collector before writing the command
|
|
75
81
|
const collectorPromise = ptyManager.armAutopilotCollector(sessionId, timeoutMs + 5000);
|
package/web/lib/ptyManager.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as pty from 'node-pty';
|
|
2
2
|
import { EventEmitter } from 'events';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
3
4
|
import { PHASE_DONE_REGEX } from '@/lib/autopilotConstants';
|
|
4
5
|
|
|
5
6
|
interface TerminalSession {
|
|
@@ -16,21 +17,42 @@ interface AutopilotCollector {
|
|
|
16
17
|
timeout: NodeJS.Timeout;
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Detect the best available shell for the current platform.
|
|
22
|
+
*/
|
|
23
|
+
function detectShell(): string {
|
|
24
|
+
if (process.platform === 'win32') return 'powershell.exe';
|
|
25
|
+
|
|
26
|
+
// Try SHELL env var first
|
|
27
|
+
const envShell = process.env.SHELL;
|
|
28
|
+
if (envShell && existsSync(envShell)) return envShell;
|
|
29
|
+
|
|
30
|
+
// Fallback chain for Unix-like systems
|
|
31
|
+
const candidates = ['/bin/zsh', '/bin/bash', '/bin/sh'];
|
|
32
|
+
for (const candidate of candidates) {
|
|
33
|
+
if (existsSync(candidate)) return candidate;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return '/bin/sh';
|
|
37
|
+
}
|
|
38
|
+
|
|
19
39
|
class PtyManager extends EventEmitter {
|
|
20
40
|
private sessions: Map<string, TerminalSession> = new Map();
|
|
21
41
|
private outputBuffers: Map<string, string[]> = new Map();
|
|
22
42
|
private autopilotCollectors: Map<string, AutopilotCollector> = new Map();
|
|
23
43
|
|
|
24
44
|
createSession(id: string, cwd: string, cols: number = 80, rows: number = 24): TerminalSession {
|
|
25
|
-
|
|
26
|
-
const shell = process.platform === 'win32' ? 'powershell.exe' : process.env.SHELL || '/bin/zsh';
|
|
45
|
+
const shell = detectShell();
|
|
27
46
|
const shellArgs = process.platform === 'win32' ? [] : ['-l'];
|
|
28
47
|
|
|
48
|
+
// Validate cwd exists, fallback to home directory
|
|
49
|
+
const safeCwd = existsSync(cwd) ? cwd : process.env.HOME || '/tmp';
|
|
50
|
+
|
|
29
51
|
const ptyProcess = pty.spawn(shell, shellArgs, {
|
|
30
52
|
name: 'xterm-256color',
|
|
31
53
|
cols,
|
|
32
54
|
rows,
|
|
33
|
-
cwd,
|
|
55
|
+
cwd: safeCwd,
|
|
34
56
|
env: {
|
|
35
57
|
...process.env,
|
|
36
58
|
TERM: 'xterm-256color',
|
package/web/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devflow-ide",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "./node_modules/.bin/next dev",
|
|
@@ -36,8 +36,6 @@
|
|
|
36
36
|
"simple-git": "^3.30.0",
|
|
37
37
|
"sonner": "^2.0.7",
|
|
38
38
|
"tailwind-merge": "^2.5.5",
|
|
39
|
-
"xterm": "^5.3.0",
|
|
40
|
-
"xterm-addon-fit": "^0.8.0",
|
|
41
39
|
"zustand": "^5.0.2"
|
|
42
40
|
},
|
|
43
41
|
"devDependencies": {
|