chore: bump version to 0.1605

This commit is contained in:
arkon
2026-02-24 13:07:18 +01:00
parent 95823cb2bd
commit 06e4a882fe
23 changed files with 2 additions and 721 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ When user says "COM":
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
**Version**: 0.1604 (must match `package.json` for npm publish)
**Version**: 0.1605 (must match `package.json` for npm publish)
## Project Overview
-83
View File
@@ -1,83 +0,0 @@
const { chromium } = require("playwright");
(async () => {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
page.setDefaultTimeout(10000);
await page.goto("http://localhost:3199", { waitUntil: "domcontentloaded" });
await page.waitForTimeout(3000);
// Click first session tab
await page.locator(".session-tab").first().click();
await page.waitForTimeout(500);
// Open session options
await page.locator(".session-tab.active .tab-gear").click();
await page.waitForTimeout(500);
// Function to check VISIBILITY of summary elements
async function checkSummaryVisibility(tabName) {
console.log("\n=== " + tabName.toUpperCase() + " TAB ===");
// Check if summary-specific elements are VISIBLE (not just in DOM)
const summarySection = page.locator(".modal:visible #summary-section, .modal:visible [id*='summary']");
const summaryVisible = await summarySection.first().isVisible().catch(() => false);
const timelineSection = page.locator(".modal:visible .run-timeline, .modal:visible [class*='timeline']");
const timelineVisible = await timelineSection.first().isVisible().catch(() => false);
const filterButtons = page.locator(".modal:visible .filter-btn, .modal:visible [class*='filter']");
const filterVisible = await filterButtons.first().isVisible().catch(() => false);
const exportBtns = page.locator(".modal:visible .export-btn, .modal:visible button:has-text('Copy'), .modal:visible button:has-text('Download')");
const exportVisible = await exportBtns.first().isVisible().catch(() => false);
console.log(" Summary section visible:", summaryVisible);
console.log(" Timeline visible:", timelineVisible);
console.log(" Filter buttons visible:", filterVisible);
console.log(" Export buttons visible:", exportVisible);
return { summaryVisible, timelineVisible, filterVisible, exportVisible };
}
// Check Respawn tab (default)
const respawn = await checkSummaryVisibility("Respawn");
// Check Context tab
await page.locator(".modal:visible .modal-tab-btn[data-tab='context']").click();
await page.waitForTimeout(200);
const context = await checkSummaryVisibility("Context");
// Check Ralph tab
await page.locator(".modal:visible .modal-tab-btn[data-tab='ralph']").click();
await page.waitForTimeout(200);
const ralph = await checkSummaryVisibility("Ralph");
// Check Summary tab
await page.locator(".modal:visible .modal-tab-btn[data-tab='summary']").click();
await page.waitForTimeout(200);
const summary = await checkSummaryVisibility("Summary");
await browser.close();
console.log("\n=== FINAL BUG ASSESSMENT ===");
const hasBugRespawn = respawn.summaryVisible || respawn.timelineVisible;
const hasBugContext = context.summaryVisible || context.timelineVisible;
const hasBugRalph = ralph.summaryVisible || ralph.timelineVisible;
const summaryWorking = summary.summaryVisible || summary.timelineVisible;
console.log("Respawn tab leaking summary content:", hasBugRespawn ? "YES - BUG EXISTS" : "No - Fixed");
console.log("Context tab leaking summary content:", hasBugContext ? "YES - BUG EXISTS" : "No - Fixed");
console.log("Ralph tab leaking summary content:", hasBugRalph ? "YES - BUG EXISTS" : "No - Fixed");
console.log("Summary tab showing its content:", summaryWorking ? "Yes - Working correctly" : "No - Check if empty or hidden");
const bugFixed = (hasBugRespawn === false) && (hasBugContext === false) && (hasBugRalph === false);
if (bugFixed) {
console.log("\n*** BUG IS FIXED - Summary content only appears in Summary tab ***");
} else {
console.log("\n*** BUG STILL EXISTS - Summary content bleeding to other tabs ***");
}
})().catch(e => { console.error(e); process.exit(1); });
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "claudeman",
"version": "0.1604",
"version": "0.1605",
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
"type": "module",
"main": "dist/index.js",
-320
View File
@@ -1,320 +0,0 @@
#!/bin/bash
# Respawn Controller Monitor for w3-reddit-analyse
# Monitors for 12 hours, logs issues and suggested fixes
SESSION_ID="5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98"
SESSION_NAME="w3-reddit-analyse"
ISSUES_FILE="/home/arkon/default/claudeman/respawn-issues.md"
FIXES_FILE="/home/arkon/default/claudeman/respawn-fixes.md"
LOG_FILE="/home/arkon/default/claudeman/respawn-monitor.log"
API_BASE="https://localhost:3000"
DURATION_HOURS=12
POLL_INTERVAL=30 # seconds
# Initialize files
cat > "$ISSUES_FILE" << 'EOF'
# Respawn Controller Issues - w3-reddit-analyse
Monitoring started: $(date)
Session ID: 5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98
## Issues Detected
EOF
sed -i "s/\$(date)/$(date)/" "$ISSUES_FILE"
cat > "$FIXES_FILE" << 'EOF'
# Suggested Fixes for Respawn Controller Issues
**DO NOT EXECUTE** - Waiting for user review
## Fixes
EOF
echo "=== Respawn Monitor Started: $(date) ===" > "$LOG_FILE"
echo "Session: $SESSION_NAME ($SESSION_ID)" >> "$LOG_FILE"
echo "Duration: $DURATION_HOURS hours" >> "$LOG_FILE"
echo "Poll interval: ${POLL_INTERVAL}s" >> "$LOG_FILE"
echo "" >> "$LOG_FILE"
# Track state for anomaly detection
PREV_STATE=""
PREV_TOKENS=0
STATE_STUCK_COUNT=0
LAST_ACTIVITY_TIME=$(date +%s)
ISSUE_COUNT=0
log_issue() {
local severity="$1"
local title="$2"
local details="$3"
ISSUE_COUNT=$((ISSUE_COUNT + 1))
echo "" >> "$ISSUES_FILE"
echo "### Issue #$ISSUE_COUNT [$severity] - $(date '+%Y-%m-%d %H:%M:%S')" >> "$ISSUES_FILE"
echo "**$title**" >> "$ISSUES_FILE"
echo "" >> "$ISSUES_FILE"
echo "$details" >> "$ISSUES_FILE"
echo "" >> "$ISSUES_FILE"
echo "[$(date '+%H:%M:%S')] ISSUE #$ISSUE_COUNT [$severity]: $title" >> "$LOG_FILE"
}
log_fix() {
local issue_num="$1"
local fix_title="$2"
local fix_details="$3"
echo "" >> "$FIXES_FILE"
echo "### Fix for Issue #$issue_num" >> "$FIXES_FILE"
echo "**$fix_title**" >> "$FIXES_FILE"
echo "" >> "$FIXES_FILE"
echo "$fix_details" >> "$FIXES_FILE"
echo "" >> "$FIXES_FILE"
}
END_TIME=$(($(date +%s) + DURATION_HOURS * 3600))
while [ $(date +%s) -lt $END_TIME ]; do
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
# Fetch session status
RESPONSE=$(curl -sk "$API_BASE/api/sessions/$SESSION_ID" 2>/dev/null)
if [ -z "$RESPONSE" ] || [ "$RESPONSE" = "null" ]; then
log_issue "CRITICAL" "API request failed or session not found" "Could not fetch session data. Server may be down or session deleted."
log_fix "$ISSUE_COUNT" "Check server status" "1. Check if claudeman-web is running: \`systemctl --user status claudeman-web\`
2. If down, restart: \`systemctl --user restart claudeman-web\`
3. Check server logs: \`journalctl --user -u claudeman-web -n 50\`"
sleep $POLL_INTERVAL
continue
fi
# Parse response
STATUS=$(echo "$RESPONSE" | jq -r '.status // "unknown"')
INPUT_TOKENS=$(echo "$RESPONSE" | jq -r '.inputTokens // 0')
OUTPUT_TOKENS=$(echo "$RESPONSE" | jq -r '.outputTokens // 0')
TOTAL_TOKENS=$((INPUT_TOKENS + OUTPUT_TOKENS))
# Always fetch respawn controller details from dedicated endpoint
RESPAWN_DETAILS=$(curl -sk "$API_BASE/api/sessions/$SESSION_ID/respawn" 2>/dev/null)
RESPAWN_ENABLED=$(echo "$RESPAWN_DETAILS" | jq -r '.enabled // false')
RESPAWN_STATE=$(echo "$RESPAWN_DETAILS" | jq -r '.state // "null"')
# Fetch detailed detection status if enabled
if [ "$RESPAWN_ENABLED" = "true" ]; then
DETECTION_STATUS=$(echo "$RESPAWN_DETAILS" | jq -c '.detection // {}')
IDLE_CONFIDENCE=$(echo "$DETECTION_STATUS" | jq -r '.confidenceLevel // 0')
CYCLE_COUNT=$(echo "$RESPAWN_DETAILS" | jq -r '.cycleCount // 0')
LAST_ACTIVITY=$(echo "$RESPAWN_DETAILS" | jq -r '.lastActivityTime // 0')
TIME_SINCE_ACTIVITY=$(echo "$RESPAWN_DETAILS" | jq -r '.timeSinceActivity // 0')
STATUS_TEXT=$(echo "$DETECTION_STATUS" | jq -r '.statusText // "unknown"')
WAITING_FOR=$(echo "$DETECTION_STATUS" | jq -r '.waitingFor // "unknown"')
AI_CHECK_STATUS=$(echo "$DETECTION_STATUS" | jq -r '.aiCheck.status // "unknown"')
AI_LAST_VERDICT=$(echo "$DETECTION_STATUS" | jq -r '.aiCheck.lastVerdict // "none"')
LAST_ERROR="none" # Respawn controller doesn't expose lastError directly
fi
echo "[$TIMESTAMP] status=$STATUS respawn=$RESPAWN_ENABLED state=$RESPAWN_STATE cycle=$CYCLE_COUNT confidence=$IDLE_CONFIDENCE tokens=$TOTAL_TOKENS" >> "$LOG_FILE"
# Check 1: Respawn not enabled when it should be
if [ "$RESPAWN_ENABLED" = "null" ] || [ "$RESPAWN_ENABLED" = "false" ]; then
if [ -z "$RESPAWN_NOT_ENABLED_LOGGED" ]; then
log_issue "INFO" "Respawn controller not enabled" "Respawn is currently disabled for this session. State: $RESPAWN_ENABLED"
log_fix "$ISSUE_COUNT" "Enable respawn controller" "Via API:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/enable'
\`\`\`
Or via web UI: Open session tab -> Respawn panel -> Enable"
RESPAWN_NOT_ENABLED_LOGGED=1
fi
else
unset RESPAWN_NOT_ENABLED_LOGGED
# Check 2: State stuck for too long
if [ "$RESPAWN_STATE" = "$PREV_STATE" ] && [ -n "$PREV_STATE" ]; then
STATE_STUCK_COUNT=$((STATE_STUCK_COUNT + 1))
# Stuck in same state for 10+ minutes (20 polls at 30s)
if [ $STATE_STUCK_COUNT -ge 20 ]; then
STUCK_MINS=$((STATE_STUCK_COUNT * POLL_INTERVAL / 60))
log_issue "WARNING" "State stuck: $RESPAWN_STATE for ${STUCK_MINS}+ minutes" "The respawn controller has been in '$RESPAWN_STATE' state for over $STUCK_MINS minutes.
Time since activity: ${TIME_SINCE_ACTIVITY}ms
Detection status: $DETECTION_STATUS"
case "$RESPAWN_STATE" in
"watching")
log_fix "$ISSUE_COUNT" "Watching state stuck" "The controller is watching but not detecting idle. Possible causes:
1. Session is actively working (expected behavior)
2. Idle detection thresholds too high
Check session activity:
\`\`\`bash
curl -sk '$API_BASE/api/sessions/$SESSION_ID' | jq '{status, lastActivityAt}'
\`\`\`
If truly idle, try adjusting config:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/config' -H 'Content-Type: application/json' -d '{\"idleThresholdMs\": 30000}'
\`\`\`"
;;
"confirming_idle")
log_fix "$ISSUE_COUNT" "Confirming idle stuck" "Stuck confirming idle state. The session may be oscillating between idle/working.
Check idle confidence:
\`\`\`bash
curl -sk '$API_BASE/api/sessions/$SESSION_ID/respawn/status' | jq '.detectionStatus'
\`\`\`
Consider increasing confirmation time or check for output patterns that reset detection."
;;
"ai_checking")
log_fix "$ISSUE_COUNT" "AI check stuck" "AI idle check is taking too long or failing.
Check AI checker status and logs:
\`\`\`bash
journalctl --user -u claudeman-web -n 100 | grep -i 'ai.*check\\|idle.*check'
\`\`\`
The AI checker spawns a separate Claude process. It may be hanging. Consider:
1. Check for stuck Claude processes: \`ps aux | grep claude\`
2. Restart the respawn controller:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/stop'
sleep 2
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/start'
\`\`\`"
;;
"sending_"*|"waiting_"*)
log_fix "$ISSUE_COUNT" "Send/wait state stuck" "Stuck in $RESPAWN_STATE - the controller is trying to send a command but may not be receiving expected response.
Check terminal output:
\`\`\`bash
curl -sk '$API_BASE/api/sessions/$SESSION_ID/terminal?tail=10000' | tail -100
\`\`\`
Try restarting respawn:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/stop'
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/start'
\`\`\`"
;;
*)
log_fix "$ISSUE_COUNT" "Unknown stuck state" "State '$RESPAWN_STATE' is stuck. Review respawn-controller.ts for this state's expected behavior and transitions."
;;
esac
STATE_STUCK_COUNT=0
fi
else
STATE_STUCK_COUNT=0
PREV_STATE="$RESPAWN_STATE"
fi
# Check 3: Error in respawn controller
if [ "$LAST_ERROR" != "none" ] && [ "$LAST_ERROR" != "null" ] && [ -n "$LAST_ERROR" ]; then
log_issue "ERROR" "Respawn controller error" "Error reported: $LAST_ERROR"
log_fix "$ISSUE_COUNT" "Resolve respawn error" "Error: $LAST_ERROR
General recovery steps:
1. Stop respawn: \`curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/stop'\`
2. Check session health: \`curl -sk '$API_BASE/api/sessions/$SESSION_ID' | jq .\`
3. Restart respawn: \`curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/start'\`
If error persists, check server logs:
\`journalctl --user -u claudeman-web -n 200 | grep -i error\`"
fi
# Check 4: Token count not progressing during active work
if [ "$STATUS" = "busy" ] && [ "$RESPAWN_STATE" = "watching" ]; then
if [ $TOTAL_TOKENS -eq $PREV_TOKENS ] && [ $PREV_TOKENS -gt 0 ]; then
TOKENS_STUCK_COUNT=$((TOKENS_STUCK_COUNT + 1))
if [ $TOKENS_STUCK_COUNT -ge 10 ]; then # 5 minutes
log_issue "WARNING" "Tokens not increasing while busy" "Session status is 'busy' but token count hasn't changed in 5+ minutes.
Current: $TOTAL_TOKENS, Previous: $PREV_TOKENS
This could indicate a hung Claude process."
log_fix "$ISSUE_COUNT" "Check for hung session" "The session may be hung. Check:
1. Session process status:
\`\`\`bash
screen -ls | grep reddit
\`\`\`
2. Attach to screen and check:
\`\`\`bash
screen -r claudeman-w3-reddit-analyse
# Press Ctrl+A D to detach
\`\`\`
3. If hung, restart the session via web UI or:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/stop'
# Then start a new session
\`\`\`"
TOKENS_STUCK_COUNT=0
fi
else
TOKENS_STUCK_COUNT=0
fi
fi
PREV_TOKENS=$TOTAL_TOKENS
# Check 5: Rapid state cycling (flapping)
if [ "$RESPAWN_STATE" != "$PREV_STATE" ]; then
STATE_CHANGES=$((STATE_CHANGES + 1))
fi
# Check every 5 minutes for flapping
if [ $((STATE_CHANGES_CHECK_COUNT % 10)) -eq 0 ] && [ $STATE_CHANGES_CHECK_COUNT -gt 0 ]; then
if [ $STATE_CHANGES -gt 20 ]; then # More than 20 state changes in 5 mins
log_issue "WARNING" "State flapping detected" "Respawn controller changed states $STATE_CHANGES times in the last 5 minutes. This indicates instability."
log_fix "$ISSUE_COUNT" "Reduce state flapping" "The controller is cycling states too rapidly. This usually means:
1. Idle detection is too sensitive
2. Session output is oscillating
Adjust detection thresholds:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/respawn/config' -H 'Content-Type: application/json' -d '{
\"idleThresholdMs\": 60000,
\"completionConfirmMs\": 15000
}'
\`\`\`"
fi
STATE_CHANGES=0
fi
STATE_CHANGES_CHECK_COUNT=$((STATE_CHANGES_CHECK_COUNT + 1))
# Check 6: High token usage without respawn cycling
if [ $TOTAL_TOKENS -gt 180000 ]; then
log_issue "WARNING" "High token usage: $TOTAL_TOKENS" "Token count is very high. Auto-compact or respawn should have triggered by now."
log_fix "$ISSUE_COUNT" "Handle high token usage" "Tokens at $TOTAL_TOKENS. Consider:
1. Manual compact:
\`\`\`bash
curl -sk -X POST '$API_BASE/api/sessions/$SESSION_ID/input' -H 'Content-Type: application/json' -d '{\"input\": \"/compact\\r\"}'
\`\`\`
2. Check auto-compact settings:
\`\`\`bash
curl -sk '$API_BASE/api/sessions/$SESSION_ID' | jq '{autoCompactEnabled, autoCompactThreshold}'
\`\`\`"
fi
fi
sleep $POLL_INTERVAL
done
echo "" >> "$ISSUES_FILE"
echo "---" >> "$ISSUES_FILE"
echo "Monitoring completed: $(date)" >> "$ISSUES_FILE"
echo "Total issues detected: $ISSUE_COUNT" >> "$ISSUES_FILE"
echo "" >> "$FIXES_FILE"
echo "---" >> "$FIXES_FILE"
echo "Monitoring completed: $(date)" >> "$FIXES_FILE"
echo "" >> "$LOG_FILE"
echo "=== Monitoring Complete: $(date) ===" >> "$LOG_FILE"
echo "Total issues: $ISSUE_COUNT" >> "$LOG_FILE"
-61
View File
@@ -1,61 +0,0 @@
/**
* Take screenshots of the typing-visible-below bug.
* Intercepts input to prevent damage to live sessions.
*/
import { chromium } from 'playwright';
async function main() {
const browser = await chromium.launch({ headless: true, args: ['--window-size=1280,800'] });
const context = await browser.newContext({ viewport: { width: 1280, height: 800 } });
const page = await context.newPage();
// Intercept input to prevent sending to real sessions
await page.route('**/api/sessions/*/input', route => route.fulfill({
status: 200, contentType: 'application/json', body: '{"success":true}'
}));
console.log('Opening Claudeman...');
await page.goto('http://localhost:3000');
await page.waitForSelector('.xterm-screen', { timeout: 10000 });
await page.waitForTimeout(3000);
// Screenshot 1: Before typing
await page.screenshot({ path: '/tmp/before-typing.png' });
console.log('Screenshot 1: /tmp/before-typing.png');
// Click on the terminal to focus it
await page.click('.xterm-screen');
await page.waitForTimeout(500);
// Type a short string
await page.keyboard.type('hello world this is a test', { delay: 30 });
await page.waitForTimeout(500);
await page.screenshot({ path: '/tmp/after-short-typing.png' });
console.log('Screenshot 2: /tmp/after-short-typing.png');
// Type a LONG string that approaches the right edge
await page.keyboard.type(' and now I am typing a very long line that should approach the right edge of the terminal to test whether text wraps incorrectly below the status bar area which would be the typing visible below bug', { delay: 15 });
await page.waitForTimeout(500);
await page.screenshot({ path: '/tmp/after-long-typing.png' });
console.log('Screenshot 3: /tmp/after-long-typing.png');
// Wait for flush + SIGWINCH to settle
await page.waitForTimeout(1000);
await page.screenshot({ path: '/tmp/after-settle.png' });
console.log('Screenshot 4: /tmp/after-settle.png (after 1s settle)');
// Try typing even MORE to really push past the edge
await page.keyboard.type(' AAAA BBBB CCCC DDDD EEEE FFFF GGGG HHHH IIII JJJJ KKKK LLLL MMMM NNNN OOOO PPPP', { delay: 10 });
await page.waitForTimeout(500);
await page.screenshot({ path: '/tmp/after-overflow.png' });
console.log('Screenshot 5: /tmp/after-overflow.png (past right edge)');
await page.waitForTimeout(1000);
await page.screenshot({ path: '/tmp/after-overflow-settle.png' });
console.log('Screenshot 6: /tmp/after-overflow-settle.png (settled)');
await browser.close();
console.log('Done');
}
main().catch(err => { console.error(err); process.exit(1); });
-71
View File
@@ -1,71 +0,0 @@
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: true,
args: ['--ignore-certificate-errors']
});
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
// Capture console messages
const consoleLogs = [];
page.on('console', msg => {
consoleLogs.push('[' + msg.type() + '] ' + msg.text());
});
console.log('Opening https://localhost:3000...');
await page.goto('https://localhost:3000', { waitUntil: 'networkidle' });
await page.waitForTimeout(2000);
console.log('Looking for shell button...');
// Click Run Shell button
const shellBtn = page.locator('button:has-text("Run Shell")');
if (await shellBtn.count() > 0) {
await shellBtn.click();
console.log('Clicked Run Shell');
} else {
console.log('Run Shell button not found');
}
await page.waitForTimeout(3000);
// Type command in terminal
console.log('Typing command...');
await page.keyboard.type('echo "tail -f /tmp/test.log"');
await page.keyboard.press('Enter');
await page.waitForTimeout(2000);
// Take screenshot
await page.screenshot({ path: '/tmp/link-test-1.png', fullPage: true });
console.log('Screenshot 1 saved');
// Move mouse over terminal area (approximate coordinates for /tmp/test.log)
const terminal = page.locator('.xterm-screen');
const box = await terminal.boundingBox();
if (box) {
console.log('Terminal at: ' + box.x + ', ' + box.y + ', ' + box.width + 'x' + box.height);
// Move mouse across the area where the path should be (roughly line 2)
const y = box.y + 50; // Approximate line 2
for (let x = box.x + 50; x < box.x + 300; x += 10) {
await page.mouse.move(x, y);
await page.waitForTimeout(50);
}
}
await page.waitForTimeout(1000);
await page.screenshot({ path: '/tmp/link-test-2.png', fullPage: true });
console.log('Screenshot 2 saved');
// Print console logs
console.log('\n=== Console Logs ===');
consoleLogs.filter(l => l.includes('LinkProvider')).forEach(l => console.log(l));
// Check if "Found links" appeared
const foundLinks = consoleLogs.some(l => l.includes('Found links'));
console.log('\nLinks detected:', foundLinks);
await browser.close();
})();
-80
View File
@@ -1,80 +0,0 @@
> claudeman@0.1490 test
> vitest run test/state-store.test.ts
 RUN  v4.0.18 /home/arkon/default/claudeman
❯ test/state-store.test.ts (33 tests | 1 failed) 57ms
✓ should create initial state when no file exists 6ms
✓ should set state file path in config 2ms
 × should debounce saves by default 15ms
✓ should write immediately with saveNow 2ms
✓ should flush pending saves 1ms
✓ should set and get sessions 1ms
✓ should return null for non-existent session 1ms
✓ should remove sessions 1ms
✓ should get all sessions 2ms
✓ should set and get tasks 1ms
✓ should return null for non-existent task 1ms
✓ should remove tasks 1ms
✓ should get all tasks 1ms
✓ should get and set Ralph Loop state 1ms
✓ should merge partial updates 1ms
✓ should get and set config 1ms
✓ should merge partial updates 1ms
✓ should reset state to initial values 1ms
✓ should preserve state file path in config 1ms
✓ should get and set ralph state 1ms
✓ should return null for non-existent ralph state 1ms
✓ should update ralph state with partial merge 1ms
✓ should create initial state on update if none exists 1ms
✓ should remove ralph state 1ms
✓ should get all ralph states 1ms
✓ should flush both main and ralph state 2ms
✓ should persist and restore state across instances 2ms
✓ should initialize global stats with zero values 1ms
✓ should add to global stats 1ms
✓ should accumulate global stats across multiple calls 1ms
✓ should increment sessions created counter 1ms
✓ should compute aggregate stats combining global and active sessions 1ms
✓ should persist global stats across instances 1ms
stderr | StateStore.saveNowAsync (/home/arkon/default/claudeman/src/state-store.ts:190:15)
[StateStore] Failed to write state file: Error: ENOENT: no such file or directory, open '/tmp/claudeman-test-1770917629701/state.json.tmp'
 at open (node:internal/fs/promises:636:25)
 at writeFile (node:internal/fs/promises:1205:14)
at StateStore.saveNowAsync (/home/arkon/default/claudeman/src/state-store.ts:180:7) {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/tmp/claudeman-test-1770917629701/state.json.tmp'
}
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯⎯⎯⎯
 FAIL  test/state-store.test.ts > StateStore > save and saveNow > should debounce saves by default
AssertionError: expected false to be true // Object.is equality
- Expected
+ Received
- true
+ false
 ❯ test/state-store.test.ts:84:40
 82| 
 83|  // Now file should exist
 84|  expect(existsSync(testFilePath)).toBe(true);
 |  ^
 85|  });
 86| 
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/1]⎯
 Test Files  1 failed (1)
 Tests  1 failed | 32 passed (33)
 Start at  18:33:49
 Duration  625ms (transform 207ms, setup 49ms, import 219ms, tests 57ms, environment 0ms)
-4
View File
@@ -1,4 +0,0 @@
{
"status": "failed",
"failedTests": []
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 KiB

-99
View File
@@ -1,99 +0,0 @@
import { chromium } from 'playwright';
async function main() {
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage();
console.log('1. Loading page...');
await page.goto('http://localhost:3000', { waitUntil: 'domcontentloaded', timeout: 10000 });
await page.waitForTimeout(1500);
console.log('2. Opening App Settings...');
await page.locator('button[title="App Settings"]').click();
await page.waitForTimeout(500);
console.log(` Modal active: ${(await page.locator('#appSettingsModal.active').count()) > 0}`);
console.log('3. Clicking Display tab...');
await page.locator('[data-tab="settings-display"]').click();
await page.waitForTimeout(300);
// Scroll the modal body to the bottom to reveal all settings
await page.locator('#settings-display').evaluate(el => el.scrollTop = el.scrollHeight);
await page.waitForTimeout(200);
console.log('4. Tab Switch Animation section...');
const header = page.locator('.settings-section-header:text("Tab Switch Animation")');
console.log(` Header visible: ${await header.isVisible()}`);
console.log('5. Checking controls...');
const toggleLabel = page.locator('#appSettingsTabFadeEnabled').locator('xpath=ancestor::label');
console.log(` ✓ Enable toggle: visible=${await toggleLabel.isVisible()}`);
for (const id of ['appSettingsTabFadeInDuration','appSettingsTabFadeInEasing','appSettingsTabFadeOutDuration','appSettingsTabFadeOutEasing']) {
console.log(` ✓ #${id}: visible=${await page.locator('#' + id).isVisible()}`);
}
console.log('6. Defaults...');
const enabled = await page.locator('#appSettingsTabFadeEnabled').isChecked();
const inDur = await page.locator('#appSettingsTabFadeInDuration').inputValue();
const inEase = await page.locator('#appSettingsTabFadeInEasing').inputValue();
const outDur = await page.locator('#appSettingsTabFadeOutDuration').inputValue();
const outEase = await page.locator('#appSettingsTabFadeOutEasing').inputValue();
console.log(` enabled=${enabled}, inDur=${inDur}, inEase=${inEase}, outDur=${outDur}, outEase=${outEase}`);
console.log('7. Modifying settings...');
// Click the toggle label (switch slider) instead of the hidden checkbox
await toggleLabel.click();
const nowChecked = await page.locator('#appSettingsTabFadeEnabled').isChecked();
console.log(` Toggle clicked, now checked=${nowChecked}`);
await page.locator('#appSettingsTabFadeInDuration').fill('500');
await page.locator('#appSettingsTabFadeInEasing').selectOption('linear');
await page.locator('#appSettingsTabFadeOutDuration').fill('200');
await page.locator('#appSettingsTabFadeOutEasing').selectOption('ease-out');
console.log(' All values changed');
// Save — scroll back up to find the save button in the footer
const saveBtn = page.locator('#appSettingsModal .modal-footer button.btn-primary');
await saveBtn.scrollIntoViewIfNeeded();
await saveBtn.click();
console.log(' Saved');
await page.waitForTimeout(500);
// Re-open to verify persistence
console.log('8. Persistence check...');
await page.locator('button[title="App Settings"]').click();
await page.waitForTimeout(500);
await page.locator('[data-tab="settings-display"]').click();
await page.waitForTimeout(300);
// Scroll down again
await page.locator('#settings-display').evaluate(el => el.scrollTop = el.scrollHeight);
await page.waitForTimeout(200);
const p = {
enabled: await page.locator('#appSettingsTabFadeEnabled').isChecked(),
inDur: await page.locator('#appSettingsTabFadeInDuration').inputValue(),
inEase: await page.locator('#appSettingsTabFadeInEasing').inputValue(),
outDur: await page.locator('#appSettingsTabFadeOutDuration').inputValue(),
outEase: await page.locator('#appSettingsTabFadeOutEasing').inputValue(),
};
const checks = [
[!p.enabled, `enabled=${p.enabled} (expect false)`],
[p.inDur === '500', `inDur=${p.inDur} (expect 500)`],
[p.inEase === 'linear', `inEase=${p.inEase} (expect linear)`],
[p.outDur === '200', `outDur=${p.outDur} (expect 200)`],
[p.outEase === 'ease-out', `outEase=${p.outEase} (expect ease-out)`],
];
let allPass = true;
for (const [ok, msg] of checks) {
console.log(` ${ok ? '✓' : '✗'} ${msg}`);
if (!ok) allPass = false;
}
console.log(`\n${allPass ? 'ALL TESTS PASSED ✓' : 'SOME TESTS FAILED ✗'}`);
await browser.close();
process.exit(allPass ? 0 : 1);
}
main().catch(err => { console.error('CRASH:', err.message); process.exit(1); });