fix: harden dashboard terminal launch helpers

This commit is contained in:
Affaan Mustafa 2026-04-14 19:44:32 -07:00
parent 48a30b53c8
commit e46deb93c8
3 changed files with 197 additions and 9 deletions

View File

@ -8,8 +8,11 @@ import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import os
import json
import subprocess
from typing import Dict, List, Optional
from scripts.lib.ecc_dashboard_runtime import build_terminal_launch, maximize_window
# ============================================================================
# DATA LOADERS - Load ECC data from the project
# ============================================================================
@ -18,6 +21,7 @@ def get_project_path() -> str:
"""Get the ECC project path - assumes this script is run from the project dir"""
return os.path.dirname(os.path.abspath(__file__))
def load_agents(project_path: str) -> List[Dict]:
"""Load agents from AGENTS.md"""
agents_file = os.path.join(project_path, "AGENTS.md")
@ -257,7 +261,7 @@ class ECCDashboard(tk.Tk):
self.project_path = get_project_path()
self.title("ECC Dashboard - Everything Claude Code")
self.state('zoomed')
maximize_window(self)
try:
self.icon_image = tk.PhotoImage(file='assets/images/ecc-logo.png')
@ -789,14 +793,9 @@ Project: github.com/affaan-m/everything-claude-code"""
def open_terminal(self):
"""Open terminal at project path"""
import subprocess
path = self.path_entry.get()
if os.name == 'nt': # Windows
subprocess.Popen(['cmd', '/c', 'start', 'cmd', '/k', f'cd /d "{path}"'])
elif os.uname().sysname == 'Darwin': # macOS
subprocess.Popen(['open', '-a', 'Terminal', path])
else: # Linux
subprocess.Popen(['x-terminal-emulator', '-e', f'cd {path}'])
argv, kwargs = build_terminal_launch(path)
subprocess.Popen(argv, **kwargs)
def open_readme(self):
"""Open README in default browser/reader"""
@ -911,4 +910,4 @@ def main():
if __name__ == "__main__":
main()
main()

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
"""
Runtime helpers for ecc_dashboard.py that do not depend on tkinter.
"""
from __future__ import annotations
import os
import platform
import subprocess
from typing import Optional, Tuple, Dict, List
def maximize_window(window) -> None:
"""Maximize the dashboard window using the safest supported method."""
try:
window.state('zoomed')
return
except Exception:
pass
system_name = platform.system()
if system_name == 'Linux':
try:
window.attributes('-zoomed', True)
except Exception:
pass
elif system_name == 'Darwin':
try:
window.attributes('-fullscreen', True)
except Exception:
pass
def build_terminal_launch(
path: str,
*,
os_name: Optional[str] = None,
system_name: Optional[str] = None,
) -> Tuple[List[str], Dict[str, object]]:
"""Return safe argv/kwargs for opening a terminal rooted at the requested path."""
resolved_os_name = os_name or os.name
resolved_system_name = system_name or platform.system()
if resolved_os_name == 'nt':
creationflags = getattr(subprocess, 'CREATE_NEW_CONSOLE', 0)
return (
['cmd.exe', '/k', 'cd', '/d', path],
{
'cwd': path,
'creationflags': creationflags,
},
)
if resolved_system_name == 'Darwin':
return (['open', '-a', 'Terminal', path], {})
return (
['x-terminal-emulator', '-e', 'bash', '-lc', 'cd -- "$1"; exec bash', 'bash', path],
{},
)

View File

@ -0,0 +1,128 @@
/**
* Behavioral tests for ecc_dashboard.py helper functions.
*/
const assert = require('assert');
const path = require('path');
const { spawnSync } = require('child_process');
const repoRoot = path.join(__dirname, '..', '..');
const runtimeHelpersPath = path.join(repoRoot, 'scripts', 'lib', 'ecc_dashboard_runtime.py');
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (error) {
console.log(`${name}`);
console.log(` Error: ${error.message}`);
return false;
}
}
function runPython(source) {
const candidates = process.platform === 'win32' ? ['python', 'python3'] : ['python3', 'python'];
let lastError = null;
for (const command of candidates) {
const result = spawnSync(command, ['-c', source], {
cwd: repoRoot,
encoding: 'utf8',
});
if (result.error && result.error.code === 'ENOENT') {
lastError = result.error;
continue;
}
if (result.status !== 0) {
throw new Error((result.stderr || result.stdout || '').trim() || `${command} exited ${result.status}`);
}
return result.stdout.trim();
}
throw lastError || new Error('No Python interpreter available');
}
function runTests() {
console.log('\n=== Testing ecc_dashboard.py ===\n');
let passed = 0;
let failed = 0;
if (test('build_terminal_launch keeps Linux path separate from shell command text', () => {
const output = runPython(`
import importlib.util, json
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
argv, kwargs = module.build_terminal_launch('/tmp/proj; rm -rf ~', os_name='posix', system_name='Linux')
print(json.dumps({'argv': argv, 'kwargs': kwargs}))
`);
const parsed = JSON.parse(output);
assert.deepStrictEqual(
parsed.argv,
['x-terminal-emulator', '-e', 'bash', '-lc', 'cd -- "$1"; exec bash', 'bash', '/tmp/proj; rm -rf ~']
);
assert.deepStrictEqual(parsed.kwargs, {});
})) passed++; else failed++;
if (test('build_terminal_launch uses cwd + CREATE_NEW_CONSOLE style launch on Windows', () => {
const output = runPython(`
import importlib.util, json
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
argv, kwargs = module.build_terminal_launch(r'C:\\\\Users\\\\user\\\\proj & del C:\\\\*', os_name='nt', system_name='Windows')
print(json.dumps({'argv': argv, 'kwargs': kwargs}))
`);
const parsed = JSON.parse(output);
assert.deepStrictEqual(parsed.argv.slice(0, 4), ['cmd.exe', '/k', 'cd', '/d']);
assert.strictEqual(parsed.argv[4], parsed.kwargs.cwd);
assert.ok(parsed.argv[4].includes('proj & del'), 'path should remain a literal argv entry');
assert.ok(parsed.argv[4].includes('C:'), 'windows drive prefix should be preserved');
assert.ok(Object.prototype.hasOwnProperty.call(parsed.kwargs, 'creationflags'));
})) passed++; else failed++;
if (test('maximize_window falls back to Linux zoom attribute when zoomed state is unsupported', () => {
const output = runPython(`
import importlib.util, json
spec = importlib.util.spec_from_file_location("ecc_dashboard_runtime", r"""${runtimeHelpersPath}""")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
class FakeWindow:
def __init__(self):
self.calls = []
def state(self, value):
self.calls.append(['state', value])
raise RuntimeError('bad argument "zoomed"')
def attributes(self, name, value):
self.calls.append(['attributes', name, value])
original = module.platform.system
module.platform.system = lambda: 'Linux'
try:
window = FakeWindow()
module.maximize_window(window)
finally:
module.platform.system = original
print(json.dumps(window.calls))
`);
const parsed = JSON.parse(output);
assert.deepStrictEqual(parsed, [
['state', 'zoomed'],
['attributes', '-zoomed', true],
]);
})) passed++; else failed++;
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
}
runTests();