mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 22:49:41 +02:00
chore: bump version to 0.1570
This commit is contained in:
+24
-11
@@ -276,7 +276,8 @@ export class SubagentWatcher extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Run pgrep once and read /proc info for all Claude PIDs in parallel.
|
||||
* Returns a Map of pid -> { environ, cmdline } for all running claude processes.
|
||||
* Returns a Map of pid -> { environ, cmdline } for subagent processes only.
|
||||
* Excludes main Claudeman-managed Claude processes (CLAUDEMAN_MUX=1).
|
||||
*/
|
||||
private async getClaudePids(): Promise<Map<number, { environ: string; cmdline: string }>> {
|
||||
const result = new Map<number, { environ: string; cmdline: string }>();
|
||||
@@ -295,6 +296,8 @@ export class SubagentWatcher extends EventEmitter {
|
||||
let cmdline = '';
|
||||
try { environ = await readFile(`/proc/${pid}/environ`, 'utf8'); } catch { /* skip */ }
|
||||
try { cmdline = await readFile(`/proc/${pid}/cmdline`, 'utf8'); } catch { /* skip */ }
|
||||
// Skip main Claudeman-managed Claude processes — only track subagents
|
||||
if (environ.includes('CLAUDEMAN_MUX=1')) return;
|
||||
if (environ || cmdline) {
|
||||
result.set(pid, { environ, cmdline });
|
||||
}
|
||||
@@ -623,20 +626,26 @@ export class SubagentWatcher extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill all subagents for a specific Claudeman session working directory
|
||||
* Kill all subagents for a specific Claudeman session.
|
||||
* IMPORTANT: Must scope to sessionId to avoid cross-session kills.
|
||||
* All sessions in the same workingDir share a projectHash, so filtering
|
||||
* by workingDir alone would kill subagents belonging to OTHER sessions.
|
||||
*/
|
||||
async killSubagentsForSession(workingDir: string): Promise<void> {
|
||||
async killSubagentsForSession(workingDir: string, sessionId?: string): Promise<void> {
|
||||
const subagents = this.getSubagentsForSession(workingDir);
|
||||
for (const agent of subagents) {
|
||||
if (agent.status === 'active' || agent.status === 'idle') {
|
||||
// Only kill subagents belonging to this specific session
|
||||
if (sessionId && agent.sessionId !== sessionId) continue;
|
||||
await this.killSubagent(agent.agentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the process ID of a Claude subagent by its session ID
|
||||
* Searches /proc for claude processes with matching session ID in environment
|
||||
* Find the process ID of a Claude subagent by its session ID.
|
||||
* Searches /proc for claude processes with matching session ID in environment.
|
||||
* Skips main Claudeman-managed Claude processes (identified by CLAUDEMAN_MUX=1).
|
||||
*/
|
||||
private async findSubagentProcess(sessionId: string): Promise<number | null> {
|
||||
try {
|
||||
@@ -653,18 +662,22 @@ export class SubagentWatcher extends EventEmitter {
|
||||
const pid = parseInt(pidStr, 10);
|
||||
if (Number.isNaN(pid)) continue;
|
||||
|
||||
let environ = '';
|
||||
try {
|
||||
// Check /proc/{pid}/environ for session ID (async read)
|
||||
const environ = await readFile(`/proc/${pid}/environ`, 'utf8');
|
||||
if (environ.includes(sessionId)) {
|
||||
return pid;
|
||||
}
|
||||
environ = await readFile(`/proc/${pid}/environ`, 'utf8');
|
||||
} catch {
|
||||
// Can't read this process's environ - skip
|
||||
}
|
||||
|
||||
// Defense-in-depth: never kill a main Claudeman-managed Claude process.
|
||||
// Main processes have CLAUDEMAN_MUX=1 in their environment; subagents don't.
|
||||
if (environ.includes('CLAUDEMAN_MUX=1')) continue;
|
||||
|
||||
if (environ.includes(sessionId)) {
|
||||
return pid;
|
||||
}
|
||||
|
||||
try {
|
||||
// Also check /proc/{pid}/cmdline for session ID (async read)
|
||||
const cmdline = await readFile(`/proc/${pid}/cmdline`, 'utf8');
|
||||
if (cmdline.includes(sessionId)) {
|
||||
return pid;
|
||||
|
||||
@@ -2441,7 +2441,7 @@ class ClaudemanApp {
|
||||
_updateLocalEchoState() {
|
||||
const settings = this.loadAppSettingsFromStorage();
|
||||
const session = this.activeSessionId ? this.sessions.get(this.activeSessionId) : null;
|
||||
const echoEnabled = settings.localEchoEnabled ?? true;
|
||||
const echoEnabled = settings.localEchoEnabled ?? MobileDetection.isTouchDevice();
|
||||
const shouldEnable = !!(echoEnabled && session);
|
||||
if (this._localEchoEnabled && !shouldEnable) {
|
||||
// Flush background-buffered keystrokes before disabling
|
||||
@@ -9935,7 +9935,7 @@ class ClaudemanApp {
|
||||
document.getElementById('appSettingsSubagentTracking').checked = settings.subagentTrackingEnabled ?? defaults.subagentTrackingEnabled ?? true;
|
||||
document.getElementById('appSettingsSubagentActiveTabOnly').checked = settings.subagentActiveTabOnly ?? defaults.subagentActiveTabOnly ?? true;
|
||||
document.getElementById('appSettingsImageWatcherEnabled').checked = settings.imageWatcherEnabled ?? defaults.imageWatcherEnabled ?? false;
|
||||
document.getElementById('appSettingsLocalEcho').checked = settings.localEchoEnabled ?? true;
|
||||
document.getElementById('appSettingsLocalEcho').checked = settings.localEchoEnabled ?? MobileDetection.isTouchDevice();
|
||||
document.getElementById('appSettingsTabTwoRows').checked = settings.tabTwoRows ?? defaults.tabTwoRows ?? false;
|
||||
// Claude CLI settings
|
||||
const claudeModeSelect = document.getElementById('appSettingsClaudeMode');
|
||||
|
||||
+165
-1
@@ -1022,7 +1022,8 @@ html.mobile-init .file-browser-panel {
|
||||
padding-right: calc(0.5rem + var(--safe-area-right));
|
||||
}
|
||||
|
||||
/* Respawn/Ralph panels - more compact on mobile, includes safe area padding */
|
||||
/* ---- Respawn Banner: Mobile Optimizations ---- */
|
||||
|
||||
.respawn-banner {
|
||||
padding: 0.4rem 0.5rem;
|
||||
padding-left: calc(0.5rem + var(--safe-area-left));
|
||||
@@ -1030,6 +1031,169 @@ html.mobile-init .file-browser-panel {
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Stack banner vertically on mobile — status on top, action log below */
|
||||
.respawn-compact-layout {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
/* Let status column shrink to fit */
|
||||
.respawn-status-col {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Wrap status row items — tokens/timer may go to second line */
|
||||
.respawn-status-row1 {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
/* Stop button — 44px touch target */
|
||||
.respawn-banner .btn-icon-only {
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Action log — horizontal strip below status, border on top instead of left */
|
||||
.respawn-action-log {
|
||||
border-left: none;
|
||||
border-top: 1px solid rgba(34, 197, 94, 0.2);
|
||||
padding-left: 0;
|
||||
padding-top: 0.25rem;
|
||||
max-height: 2em;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
/* Countdown timers — tighter on mobile */
|
||||
.respawn-countdown-timers {
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.respawn-countdown-timer {
|
||||
font-size: 0.55rem;
|
||||
padding: 0.05rem 0.25rem;
|
||||
}
|
||||
|
||||
/* ---- Session Options Modal: Respawn Tab Mobile Optimizations ---- */
|
||||
|
||||
/* Respawn header — stack status and buttons vertically */
|
||||
.respawn-header {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* Enable/Stop buttons — full-width touch targets */
|
||||
.respawn-header .respawn-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.respawn-header .respawn-actions .btn-toolbar {
|
||||
flex: 1;
|
||||
min-height: 44px !important;
|
||||
font-size: 0.85rem !important;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Duration preset buttons — 44px touch targets in a grid */
|
||||
.duration-presets {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.duration-preset-btn {
|
||||
min-height: 40px;
|
||||
padding: 0.5rem 0.4rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Custom duration — spans full width below the grid */
|
||||
.duration-custom {
|
||||
grid-column: 1 / -1;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.duration-custom .duration-preset-btn {
|
||||
flex: 0 0 auto;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.duration-custom-input.visible {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.duration-custom-input input {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
}
|
||||
|
||||
/* Preset selector row — stack vertically */
|
||||
.preset-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.preset-selector select {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
font-size: 16px; /* Prevents iOS zoom */
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.preset-selector .btn {
|
||||
flex: 1;
|
||||
min-height: 40px;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Textareas — prevent iOS zoom with 16px font */
|
||||
#sessionOptionsModal textarea {
|
||||
font-size: 16px;
|
||||
min-height: 56px;
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
/* Checkboxes — larger touch area */
|
||||
#sessionOptionsModal .checkbox-inline {
|
||||
min-height: 40px;
|
||||
font-size: 0.85rem;
|
||||
padding: 0.25rem 0;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .checkbox-inline input[type="checkbox"] {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
/* Respawn options row — stack if needed */
|
||||
#sessionOptionsModal .respawn-options-row {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Form section headers — more breathing room */
|
||||
#sessionOptionsModal .form-section-header {
|
||||
margin-top: 1rem;
|
||||
padding-top: 0.75rem;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.ralph-panel {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
+2
-2
@@ -4011,10 +4011,10 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
|
||||
session.ralphTracker.stopWatchingFixPlan();
|
||||
}
|
||||
|
||||
// Kill all subagents spawned by this session
|
||||
// Kill all subagents spawned by this session (scoped to sessionId to avoid cross-session kills)
|
||||
if (session && killMux) {
|
||||
try {
|
||||
await subagentWatcher.killSubagentsForSession(session.workingDir);
|
||||
await subagentWatcher.killSubagentsForSession(session.workingDir, sessionId);
|
||||
} catch (err) {
|
||||
console.error(`[Server] Failed to kill subagents for session ${sessionId}:`, err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user