@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  const path = require('node:path');
2
2
 
3
- const VERSION = '0.8.3';
3
+ const VERSION = '0.9.0';
4
4
 
5
5
  // Root of the installed npm package (where source files live)
6
6
  const PACKAGE_ROOT = path.resolve(__dirname, '..');
package/lib/web.js CHANGED
@@ -49,15 +49,27 @@ async function webCommand(options) {
49
49
  process.exit(1);
50
50
  }
51
51
 
52
- // 5. Start the web server
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 .next doesn't exist
63
+ // Build first if not dev mode and no valid production build exists
57
64
  if (!isDev) {
58
- const nextDir = path.join(webDir, '.next');
59
- if (!fs.existsSync(nextDir)) {
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:${port}`;
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.8.3",
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-${sessionId}-${agent}-${Date.now()}.md`);
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);
@@ -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
- // Determine shell based on platform
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.7.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": {