Skip to content

Releases: Ark0N/Codeman

codeman@0.7.0

25 May 22:20

Choose a tag to compare

Minor Changes

  • Response viewer & terminal-stability improvements, plus test/error-handling hardening.
    • Copy button on code blocks (#98): Every fenced code block in the response viewer now has a one-click copy button pinned to its top-right, outside the <pre> scroll container so it stays put during horizontal scroll. ASCII diagrams keep their line-wrap toggle alongside it. Copy prefers the async Clipboard API and falls back to a hidden-textarea + execCommand path, so it works over plain HTTP (tunnel) too, with a brief ✓/✕ feedback state.
    • Fix: stop auto-sending Ctrl+L from session-selection paths (#99): A fast page refresh or SSE reconnect could fire two programmatic Ctrl+L (\x0c) sends within Claude Code 2.x's "clear conversation" confirmation window, silently wiping the active conversation. Removed the automatic Ctrl+L sends from selectSession(), restoreTerminalSize(), and the dead sendPendingCtrlL() path; redraws now rely on resize/SIGWINCH. User-initiated Ctrl+L still works. Trade-off: an occasional transient stale Ink frame right after refresh that self-heals on the next keypress — far preferable to silent data loss.
    • Test & error-handling hardening (#97): Repaired route-test harness error rendering via a dedicated route-error-handler.ts, and stopped the AI idle/plan checkers from spawning real processes during tests.

codeman@0.6.12

25 May 21:50

Choose a tag to compare

Patch Changes

  • Fix new-session crash after a tmux upgrade and isolate Codeman sessions on a dedicated tmux socket.
    • Pane file-descriptor limit: raise ulimit -Sn before launching the CLI (in both the spawn and respawn paths) so the newer tmux + macOS launchd combination — which hands panes a low soft nofile limit (256) that recent Claude Code refuses to start under — no longer kills every freshly spawned session on startup.
    • Single-socket isolation: all Codeman-owned tmux sessions now live on a dedicated socket (tmux -L codeman, overridable via CODEMAN_TMUX_SOCKET), fully separated from the user's default tmux server. The socket name is validated and shell-escaped at every call site.
    • Drop the drift-prone per-session tmuxSocket field: session reconciliation collapses to a single list-panes query against the one socket, eliminating live sessions being wrongly marked dead ("session not found") and duplicate "Restored:" tabs. Stale per-session socket tags and duplicate records are cleaned from disk on load (dedup by muxName, keeping the real entry over restored- placeholders).
    • Route remaining bare-tmux call sites through the socket: the window-size query on re-attach (previously fell back to 120×40 and lost scrollback) and the send-key route (Shift+Enter / Ctrl+Enter newline).
    • SSH chooser scripts (tmux-manager.sh, tmux-chooser.sh) route every tmux call through the dedicated socket.

codeman@0.6.11

19 May 14:44

Choose a tag to compare

Patch Changes

  • Resume Conversation: fixes and folder drill-down.
    • fix(history): decodeProjectKey() now uses longest-join-first backtracking with on-disk validation, so sibling directories sharing a prefix (e.g. diary/ vs diary-app/) resolve to the correct path. Previously the greedy shortest-match decoder picked the shorter name and bailed, surfacing $HOME in the Resume Conversation list and resuming into the wrong folder. Greedy decode is kept as a fallback so history for deleted projects still resolves. (#92)
    • fix(tabs): Drop the client-side resurrection of ended-session tabs. The old code cached open tabs in localStorage and rebuilt them as grayed-out stubs whenever the server no longer knew them, which left phantom tabs after closing a session on another device. The server is now the single source of truth; legacy localStorage keys are purged on init. Net -44 / +6 lines. (#93)
    • feat(history): New "View all in this folder" drill-down on Resume Conversation. GET /api/history/sessions accepts projectKey (validated against ^[A-Za-z0-9_-]+$ before any filesystem access), offset, and limit; single-folder mode bypasses the 50-cap and returns { sessions, total }. Frontend adds a modal listing 20 sessions per page with a "Show more" pagination button. Modal items omit their own "View all" button to prevent recursive entry points. (#94)

codeman@0.6.10

19 May 10:12

Choose a tag to compare

Patch Changes

  • Security: paste-image endpoint hardening (#90)

    Addresses seven findings from the dismissed review of #84. Most exposed in tunneled deployments where CODEMAN_PASSWORD is set but the server is reachable beyond localhost.

    • CSRF protection on POST /api/sessions/:id/paste-image. Requires Origin/Referer to match req.host; non-browser clients (no Origin and no Referer) must send X-Codeman-CSRF. Defeats cross-origin <form enctype="multipart/form-data"> submits that would otherwise plant arbitrary bytes into the victim's .claude-images/ while their session cookie is live.
    • Magic-byte validation on uploaded images. Sniffs the first 12 bytes against PNG/JPEG/GIF/WebP/BMP signatures and rejects 415 on mismatch. Polyglot HTML-or-SVG-with-image-MIME no longer round-trips through the endpoint.
    • Symlink-safe writes on .claude-images/. lstat before the write, non-recursive mkdir, O_EXCL|O_NOFOLLOW on file open. A node_modules postinstall (or the agent itself) planting .claude-images -> ~/.ssh/ no longer redirects pastes outside workingDir.
    • Multipart parser swap to @fastify/multipart with limits: { fileSize: 10MB, files: 1, fields: 4 }. Replaces a hand-rolled boundary scanner that matched the literal boundary anywhere in the body, hard-coded \r\n (silently corrupting LF-only clients), and had no part-count cap.
    • Rate limit + GC: token-bucket (30/min per IP+session) and hourly GC of paste-* files older than 7 days from each live session's .claude-images/. New paste-image-gc.ts started/stopped from WebServer.start/stop.
    • Collision-free filenames: paste-${Date.now()}-${randomBytes(4)}${ext}. Two tabs pasting in the same millisecond no longer silently last-write-wins.
    • Bracketed-paste preservation: text-only paste in image-input.js now goes through terminal.paste(text) instead of sendInput(text), so xterm preserves CSI 200~ ... CSI 201~ markers — Claude Code uses them as part of its prompt-injection defenses.

    Fix: duplicate multipart parser conflict

    Removed a duplicate multipart content-type parser left behind after the swap above. The duplicate registration conflicted with @fastify/multipart's own parser; uploads now flow through the plugin exclusively.

    WebGL renderer auto-fallback hardening (#91)

    Follow-ups on the longtask auto-fallback shipped in #83.

    • PerformanceObserver is now disconnected on onContextLoss as well as on the trip path. Previously the observer outlived its disposed addon after a context loss, holding a closure reference over every longtask the page emitted.
    • Thresholds (200ms / 3 longtasks / 30s window / 5s grace / 7d sticky-disable) are hoisted to WEBGL_FALLBACK in constants.js. No more inline literals.
    • New evaluateWebGLLongTaskTrip() pure helper splits the rolling-window arithmetic from the PerformanceObserver callback so the trip math is unit-testable. New test/webgl-fallback.test.ts (9 tests, port 3166): trip inside window, no-trip when spread, sub-threshold filtering, stale-entry pruning, cumulative counting across batches, observer-dispose idempotency.

    CI: server boot smoke test

    GitHub Actions now boots the server as a final step after typecheck/lint/format. Catches production-only ESM/CJS regressions that tsx masks in dev.

    Docs

    CLAUDE.md frontend-module table updated to include image-input.js (overlooked when #84 landed).

codeman@0.6.9

17 May 04:07

Choose a tag to compare

Patch Changes

  • Terminal renderer hardening, SSE bandwidth cut, image paste, and a security tightening on the new live filter:
    • Multi-primitive yield for write pacing (#85): replaces six raw requestAnimationFrame callsites in the xterm.js write pipeline with a yielding helper that races requestAnimationFrame, setTimeout(50), and a tick Worker. Keeps the terminal responsive when the tab is backgrounded or occluded — Chrome's intensive-throttling no longer stalls long writes.
    • WebGL longtask auto-fallback (#83): a PerformanceObserver watches for ≥200ms WebGL frames; three within a 30s window disposes the WebGL addon and falls back to the canvas renderer. Decision is persisted in localStorage for 7 days, and ?webgl=force clears it.
    • Per-client live SSE subscription filter (#86): each connected client gets a stable UUID and can narrow its terminal stream to one session via POST /api/events/subscribe — no EventSource reconnect on tab switches. Cuts SSE bandwidth roughly N× when N sessions are open. Lifecycle/metadata events (session:*, case:*, ralph:*, hook:*) now broadcast to every client so sidebars stay in sync.
    • Image paste and drag-and-drop into the terminal (#84): Ctrl+V and dropped images upload to POST /api/sessions/:id/paste-image, save under ${workingDir}/.claude-images/paste-${ts}.${ext} and type the path into the terminal. Hard 10MB cap, server-generated filename (no traversal), .svg deliberately excluded from the allowlist to avoid a same-origin XSS path through file-raw.
    • SSE clientId validation: the per-client identifier introduced in #86 is now constrained to [A-Za-z0-9_-]{8,64} at both ingress points. Without this, an authenticated attacker could send another tab's clientId to silently evict it from broadcasts, mutate any clientId's session filter to blackhole the victim's terminal stream, or grow sseClientsById unboundedly via long IDs. The subscribe payload is also capped at 64 session entries of ≤128 chars each.

codeman@0.6.8

12 May 08:26

Choose a tag to compare

Patch Changes

  • Finish the hostname-aware notification plumbing started in 0.6.7 and lock down the recent UI/runtime fixes with regression tests.
    • Browser Notification API (OS-level desktop pop-ups, layer 3 of the 5-layer notification system) now uses ${originalTitle}: ${title} instead of the hardcoded Codeman: literal — so multi-host users running Codeman on laptop / dev box / NAS see codeman:<host>: <event> consistently across tab title, tab-flash, Web Push, and OS notifications.
    • Inline session rename hardened against three corner cases: IME composition commits (Chinese pinyin Enter no longer ships half-composed text as the session name), mid-rename SSE deletion (orphaned <input> no longer 404s on blur), and double-fire on stuck settle-once flag (closure-local settled boolean replaces the boolean instance flag).
    • Test coverage backfilled for two prior shipped fixes:
      • <title>codeman:<host></title> server-side templating (#82): 8 tests covering default os.hostname(), --title-hostname override, HTML-escape against <script>-style breakout, ampersand non-double-encoding, and template-tail byte-identical invariance.
      • tmux size-query helper (#80): 15 tests covering the browser-resize-between-attaches happy path, the query-then-die race, zero/negative/empty/non-numeric output fallbacks, and argv-form/timeout assertions that lock down the no-shell-interpolation guarantee. Inline 14-line query block extracted into a named queryTmuxWindowSize() export in session.ts so the test surface is a pure function.
    • Regression coverage added for stripInkRedrawBloat route helper.
    • CLAUDE.md and README.md updated to document dual-CLI env-prefix discipline (CLAUDE_CODE_* vs OPENCODE_*), the xterm-zerolag-input published-package side-effect of overlay edits, and the unified hostname prefix across tab title / tab-flash / OS notifications.

codeman@0.6.7

12 May 07:19

Choose a tag to compare

Patch Changes

    • fix(client): preserve inline rename input across tab re-renders (#81) — Right-click → rename on a session tab no longer loses keystrokes when SSE traffic from sibling sessions triggers a tab re-render. Adds an _inlineRenameActive guard at the top of renderSessionTabs() and _fullRenameSessionTabs() so the in-progress input isn't destroyed mid-typing. Also fixes a latent double-fire of finishRename (blur + Enter could both invoke it). Drive-by: safer DOM child clearing in place of innerHTML = ''.
    • feat: hostname-aware window title (#82) — The browser tab title is now codeman:<hostname> instead of the bare Codeman literal, so users running Codeman on multiple hosts (laptop, dev box, NAS) can tell at a glance which tab points at which backend. New --title-hostname <name> CLI flag overrides the detected os.hostname() when it's noisy or you want a cosmetic name. The title is templated into the served HTML on first byte (with narrow HTML escaping), so it's correct from the first paint and works without JavaScript. Title-flash logic now respects the per-host title.
    • perf: larger terminal tail on tab switchTERMINAL_TAIL_SIZE raised from 128KB to 1MB. When switching back to a busy session tab you now get ~8× more scrollback restored immediately.
    • fix: preserve response text in Ink redraw strippingstripInkRedrawBloat() rewritten from a first-VPA approach to cluster-based detection. The previous algorithm assumed all VPA escapes after the first one belonged to a single redraw region and discarded everything in between, which silently lost 100KB+ of legitimate Claude response text once a render had occurred. The new approach groups VPAs into clusters separated by ≥8KB gaps and only collapses clusters spanning ≥32KB, so streamed response content between redraw bursts is preserved.
    • docs: CLAUDE.md Additional Commands gains the --title-hostname row; README.md gets a "Hostname-Aware Window Title" subsection under Multi-Session Dashboard.

codeman@0.6.6

11 May 20:42

Choose a tag to compare

Patch Changes

  • Terminal scrollback significantly increased — both the xterm.js viewport and the tmux backing buffer were bottlenecking how far back you could scroll. Three changes:

    • DEFAULT_SCROLLBACK raised from 20000 → 50000 lines (xterm.js, main terminal). The previous bump from 5000 only helped users with empty localStorage; existing users were stuck on whatever value they first picked up. The loader now treats DEFAULT_SCROLLBACK as a floor — if your stored value is below the new minimum, you're raised to it automatically.
    • Subagent / teammate terminals (panels-ui.js) were stuck at 5000; now use the same DEFAULT_SCROLLBACK constant (50000).
    • New tmux sessions now run with history-limit 50000 (tmux defaults to 2000). This matters for hard-reload / re-attach — without it, only the last ~2000 lines survive the round-trip back into a fresh xterm.

    Tmux flicker on session re-attach fixed (PR #80 by @aakhter): the PTY now queries the existing tmux window size via tmux display -p before spawning, instead of hardcoding 120x40. Previously, every re-attach forced tmux to resize down to 120x40, causing a visible flicker and one frame of scrollback loss. The -x 120 -y 40 flag was also dropped from tmux new-session so the initial size matches the first attaching client. Uses execFileSync (not shell) for safety and falls back to 120x40 on any error.

    Docs: CLAUDE.md now documents two recurring foot-guns — the xterm-zerolag-input overlay code is duplicated between packages/xterm-zerolag-input/src/ and inline inside src/web/public/app.js, so any overlay change must touch both; and the COM workflow explicitly includes a post-push gh run watch step to confirm CI before considering the release done.

codeman@0.6.5

09 May 01:24

Choose a tag to compare

Patch Changes

  • Mobile fix

    • Android virtual keyboard: space character was silently dropped on touch devices using GBoard / SwiftKey / similar IMEs. Root cause: the input-event handler in terminal-ui.js treated any whitespace-only textarea value as proof that xterm had already processed the input. A lone space (' '.trim() === '') tripped this guard, so the space was consumed but never forwarded. Now skips only when the textarea is truly empty (or whitespace from a non-space key). Reported and diagnosed by @coolk8 in #79.

    Docs

    • CLAUDE.md: added Zod .optional()-vs-null gotcha (recurring trap from 0.6.3 / 0.6.4 incidents) and a more visible warning against running bare npm test (kills the host tmux session).
    • docs/local-echo-overlay-plan.md: marked SHIPPED, corrected xterm version reference (v5.3.0 → @xterm/xterm ^6.0.0).

codeman@0.6.4

07 May 11:17

Choose a tag to compare

Patch Changes

  • Fix "Failed to enable respawn: Invalid request body" error when selecting infinity duration (∞) in the respawn modal. Frontend was sending durationMinutes: null, which Zod's .optional() schema rejected (it accepts undefined only). The body now omits the field when no duration is selected.