Skip to content

Releases: Ark0N/Codeman

codeman@1.9.3

Choose a tag to compare

@github-actions github-actions released this 30 Jul 12:28

Patch Changes

  • Plan-usage chip now defaults ON on desktop, plus the reworked xterm-zerolag-input README.
    • Plan-usage chip defaults ON (desktop). The showPlanUsageLimits chip (live 5-hour and weekly plan usage from the Claude statusline) used to be opt-in and default OFF, so most users never saw it. Desktop now defaults ON; handhelds still default OFF so the phone header stays minimal and the mobile-header-buttons-policy guard keeps passing. Devices with an explicitly stored preference keep whatever they chose, so nobody's OFF gets overridden.
    • One resolver behind the chip. Added planUsageChipEnabled() in settings-ui.js and routed all three call sites through it: the App Settings checkbox, the chip's visibility, and the create-time statusLineTelemetry flag in session-ui.js. Those three had independent ?? false / === true defaults, and a chip revealed without the telemetry flag renders forever, so a default flip on one site alone would have shipped a permanently empty chip.
    • Cron button comment corrected. The App Settings comment claimed "Cron button defaults ON" while the code, the template (btn-cron--hidden) and the CSS all default it OFF. Verified against a fresh browser profile: the button is hidden and its checkbox unchecked out of the box. Comment now matches, and states why the two halves stay consistent.
    • Docs. CLAUDE.md, docs/architecture-invariants.md and docs/usage-limits-display-plan.md updated for the new default and the single-resolver rule; the stale styles.css comment claiming the server strips the chip's hidden class at render was corrected (display is per-device, so the client reveals it).
    • xterm-zerolag-input README rework (0.1.5 shipped the content; this republishes with the graphic and promo changes): replaced the misaligned 8-line keystroke-flow diagram with a two-line stock-vs-zerolag contrast, added a Codeman callout above the demo GIF with links to getcodeman.com and the repo, and rewrote the Origin section so it argues the extraction story instead of repeating the promo.

codeman@1.9.2

Choose a tag to compare

@github-actions github-actions released this 30 Jul 10:46

Patch Changes

  • Rewrite the xterm-zerolag-input package README as a value-first document and correct the drift that had accumulated against the source.

    • Added the side-by-side phone demo GIF (docs/images/zerolag-demo-20260728.gif) as the hero image, referenced by absolute raw URL so it renders on npmjs.com as well as GitHub. The two-phone comparison shows 0ms local echo next to a 600ms-2.7s server echo on the same session.
    • New "Why this one" comparison table, an explicit list of target use cases (SSH web clients, cloud IDEs, mobile terminals, container consoles), and a bundle-size badge (6.1 kB gzipped, measured from the ESM build).
    • Corrected the test-count badge from 78 to the actual 175 tests across 5 files, in both the package README and the Published Packages section of the root README.
    • Removed the stale "Unicode/emoji rendered at single-cell width" limitation. CJK, fullwidth forms and emoji have had double-width rendering and visual-column positioning since the wide-character fix; the honest remaining caveat (per-code-point width summing over-counts ZWJ grapheme clusters) replaces it.
    • Documented the previously undocumented public setPrompt() method for switching prompt strategies at runtime, and the new "Wide characters (CJK, emoji)" integration section covering the optional Unicode11Addon path and the built-in range-table fallback.
    • Documented backgroundColor: 'transparent', corrected the foregroundColor default, and updated the grid-alignment math to reflect visual-column positioning rather than character index.

    No source changes, docs only.

codeman@1.9.1

Choose a tag to compare

@github-actions github-actions released this 29 Jul 15:51

Patch Changes

  • Narrow the Run dropdown, and close the last two gaps in web-tab asset rewriting.

    The Run dropdown was pinned at its full width. It capped at 300px, and the recent-session rows wanted 326px, so it always rendered at the cap and reached further across the terminal than it needed to. Now 250px, chosen as the width at which a ~/<dir>/<repo> + timestamp row still fits whole, since identifying a session to resume is what that list is for. Three fixes were needed to make the narrower menu degrade instead of clip: the saved-URL label now has its own element, because text-overflow on the row button did nothing (a bare text node inside a flex container becomes an anonymous flex item that ellipsis cannot reach); .hist-dir got min-width: 0, without which a flex item refuses to shrink below its own text and pushes the date out of the box; and history rows are held to the container width, because the list's overflow-y: auto implicitly makes overflow-x: auto and let each row size to its own content and scroll sideways. Phone and tablet widths are unchanged, being set separately in mobile.css.

    A dashboard's own /api/... assets are relayed again. The Referer-keyed 404 fallback, which rescues a root-absolute asset that no rewrite layer could reach, refused everything under /api outright. Dashboards commonly serve their assets from exactly that namespace, so those requests had no rescue at all. The refusal is now precise: the relay runs before the API-shaped 404, and the auth exemption refuses only paths that resolve to a REAL Codeman route, with /ws/ and /q/ still refused by prefix.

    Two findings shaped that fence, both from probing Fastify rather than reading it. hasRoute() matches the registered PATTERN literally, so /api/sessions/abc reports no match against a registered /api/sessions/:id and would have granted an unauthenticated exemption on a live session-scoped route; findRoute() performs the real lookup and is what the fence uses. And @fastify/static is mounted at /, so it registers a root catch-all matching every path, which has to count as "no real route" or the fence would refuse every referer-form request and break the rescue that already worked. A root catch-all is distinguishable because it is the only route whose wildcard param comes back equal to the whole request path. The fence fails closed, and both edges are pinned in test/webview-auth-exemption.test.ts.

    url() inside runtime CSS is rewritten. Measuring the fallback against a purpose-built dashboard showed one sink no relay can reach: a <style> element built by page script has no URL of its own, so the browser sends an EMPTY Referer with the image request it triggers. The injected URL shim now rewrites root-absolute url() in <style> blocks, both as markup and when a <style> node is inserted. Verified in Chromium: a stylesheet-only /api/hero.png and a runtime <style> /api/late.png both load, where both previously failed. The remaining known gap is self-navigation via location.href, which cannot be patched because Location.href is unforgeable.

codeman@1.9.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 07:02

Highlights

  • 📱 Browse the filesystem from your phone: a new root-confined path picker in Link Existing and the mobile keyboard bar (📁 key). Tap through directories, insert a file path into the prompt without submitting it, preview images/documents inline, and clear an unsent prompt with one tap.
  • 🗂️ Web tabs got manageable: edit or delete saved dashboard URLs straight from the Run dropdown, and images inside proxied dashboards no longer 404 (the runtime shim now covers DOM URL sinks; 0 → 23 of 23 viewport images decoding on a real dashboard).
  • 🧾 Response viewer normalization: Claude conversations now render at real turn boundaries instead of one card per transcript row. On a 40-transcript corpus: 3,108 cards became 621 with no assistant text lost.
  • 🔒 Multi-user hardening: two scoping holes in the new path picker closed (ownership check + per-user browse roots), regression-guarded.

Full notes below.


Minor Changes

  • 2667150: feat(mobile): browse and insert local file and folder paths

    Add a root-confined filesystem picker to Link Existing and the extended mobile
    keyboard bar. Selected paths remain editable at the active prompt, supported
    images/documents/text files open in a safe inline preview, and a new one-tap
    action clears only the current unsent input without invoking /clear.

Patch Changes

  • 3cff98f: Fix two multi-user scoping holes in the new filesystem path picker. GET /api/filesystem/browse and GET /api/filesystem/preview accept an optional sessionId that contributes the session's working directory as a browse root, but they resolved it straight off the session map without an ownership check, unlike the nine other session-scoped handlers in the same route file. A non-admin could therefore pin another user's working directory as a root simply by passing their session id, then list and preview files under it. Both endpoints now run canAccessOwned and report 404, which also avoids confirming that a session id exists.

    Separately, Home and CASES_DIR were unconditional browse roots for every caller. Per-user spaces live at <USER_SPACES_DIR>/<username>, which is inside homedir(), so the Home root alone exposed every other user's workspace to any authenticated user. In multi-user mode a non-admin now gets only their own space plus anything explicitly listed in CODEMAN_FILE_PICKER_ROOTS; /mnt/d is no longer offered by default, since a broad host mount should be an explicit operator decision in a multi-user deployment. Admins keep the host-wide roots, and single-user mode is unchanged.

    Both holes are regression-guarded in test/routes/file-routes.test.ts, verified to fail against the previous code. Multi-user mode is opt-in and off by default, so single-user installs were never affected.

  • Web tabs: delete saved URLs from the Run dropdown, and fix images in proxied dashboards.

    Saved URLs are now manageable from the dropdown. Each row under "Web / URL" gains a gear and an x, so a URL can be edited or deleted without first opening it as a tab. Previously the only delete path ran through the gear on an open tab, which was a dead end for a URL you no longer wanted open at all. Both controls stay permanently visible rather than hover-revealed, because the same menu is used on touch, and they get a larger hit box there. Deleting leaves the dropdown open on the remaining rows, and deleting the dashboard that is currently open also closes its tab and unmounts its frame.

    Runtime-injected images no longer 404. A dashboard that renders its own markup from script (card.innerHTML = '<img src="/api/hero?slug=x">', img.src = '/api/slide') escaped every rewrite layer at once: <base href> never applies to a root-absolute URL, the server-side attribute rewrite only ever sees the initial document, and runtimeUrlShim() patched only fetch, XMLHttpRequest, WebSocket and EventSource. Those requests landed on Codeman's own root and 404'd, with a symptom that reads as an upstream fault: the dashboard's data loaded while every image stayed broken.

    The shim now also covers the DOM URL sinks, so the request is never emitted in the first place and neither the /api fence in the 404 fallback nor the one in the auth middleware had to move. It wraps innerHTML, outerHTML, insertAdjacentHTML (including on ShadowRoot), setAttribute/setAttributeNS, and the src/srcset/href/poster/data/action property setters on img, source, media, video poster, script, iframe, embed, track, link, anchor, area, object and form, with a MutationObserver as a last net for sinks not patched above. Every rewrite routes through the same idempotent helper, which matters because unlike the server-side rewrite this one sees markup that may already be proxied, and a page re-injecting its own outerHTML would otherwise double-prefix. Everything is defensively guarded and marked so a double injection cannot wrap an already-wrapped setter.

    Measured against a real dashboard: 693 image elements, 0 of them under the proxy prefix and 0 of 23 in-viewport images decoded before, 693 and 23 of 23 after. Covered by a new jsdom suite over the shim's DOM half and a new frontend suite over the dropdown rows. Known remaining gaps are documented in docs/web-tabs.md: a root-absolute url() inside a stylesheet injected at runtime, and self-navigation via location.href, which cannot be patched because Location.href is unforgeable.

    Also in this release: a value-first README overhaul pointing at getcodeman.com, and the QR-auth distribution test now uses a chi-square check instead of a max-deviation threshold that failed on random variance.

  • bca56b4: Normalize Claude conversations in the response viewer. A Claude transcript is an append-only event log, so one logical exchange spans many JSONL rows: tool-result rows, meta/image/skill rows, compact summaries, task and team notifications, sidechains, replayed assistant snapshots, and multi-block assistant output. The viewer rendered a card per row, which produced duplicate and truncated cards that read as lost responses. Cards are now built at real human-turn boundaries, replayed assistant snapshots are deduplicated, and sidechain rows (which belong to subagents, not the main conversation) no longer leak in. An identical prompt that legitimately recurs after an assistant reply is still kept as its own turn.

    Measured over 40 real transcripts: 3108 cards became 621, duplicate cards dropped from 74 to 8 (all of them genuinely repeated turns), no assistant text was lost, and the non-context=full last-response text was byte-identical on every file.

    Also rebinds recovered sessions to their transcript. reconcileSessions() can recover a lost mux session as a restored-<uuid8> placeholder with a stale working directory, which made transcript lookup by cwd find nothing. The placeholder still carries the first eight characters of the conversation UUID, so the viewer now rebinds to the matching top-level transcript when exactly one candidate matches.

codeman@1.8.3

Choose a tag to compare

@github-actions github-actions released this 27 Jul 23:12

Patch Changes

  • 8c089a4: Add four light UI and terminal skins: Paper Gray, Solarized Light, Catppuccin Latte, and Rosé Pine Dawn. The Skin picker now groups Light and Dark options, and each light skin ships a matching xterm ANSI palette plus color-scheme: light so native selects, date pickers and scrollbars stop rendering as dark OS widgets on a light page. Terminals set minimumContrastRatio: 4.5 under a light skin (main terminal and teammate terminals both), which keeps CLI output that assumes a dark background readable, and applyTerminalSkin() now refreshes the zero-lag input overlay so typed-but-unflushed text does not keep the previous theme's colors.

    Elevated surfaces (modals, command palette, dropdowns, subagent and ultracode windows, file preview, attachment tray, mobile sheets) now resolve through shared --floating-bg / --control-* / --banner-bg-* / --modal-backdrop / --elevated-shadow tokens instead of hardcoded near-black rgba, so they follow whichever skin is active. On the Daylight skins this lifts modals slightly off the page background; OG Codeman pins its own near-black value to keep that palette neutral.

    Also defines twelve CSS compatibility aliases (--bg-primary, --bg-secondary, --bg-tertiary, --text-primary, --text-secondary, --border-color, --accent-color, --success, --error, --danger, --font-mono, --shadow-lg) that panels and overlays already referenced in about 79 places but which were never actually declared, so those rules silently resolved to nothing. Status badges and accent-tinted pills (search filter chips and result badges, session tab mode pills, respawn state, Ralph priority and circuit-breaker badges, tunnel and voice status, mobile case picker) no longer keep their pale light-on-dark ink under a light skin, where it measured 1.0 to 1.9:1 and made the search filter chips invisible.

    New static regression test/skin-themes.test.ts guards the four-way parity between the CSS token block, the xterm palette, the pre-paint allowlist and the Settings picker.

codeman@1.8.2

Choose a tag to compare

@github-actions github-actions released this 27 Jul 20:29

Patch Changes

  • Web tabs: open dashboard URLs as tabs beside agent sessions, plus terminal link fixes.

    Web tabs. The Run dropdown gains a "Web / URL" section. A saved URL renders as a tab in the same strip as Claude/Codex/Gemini sessions, with the same Alt+1-9 numbering, an icon picker, and per-device tab order. Frames stay mounted while hidden (LRU-bounded), so switching tabs never reloads a dashboard.

    Dashboards are proxied through Codeman's own origin, because a direct iframe fails three ways at once: an HTTPS Codeman cannot embed a plain-HTTP target (mixed content, with no override at all on iOS Safari), many dashboards send X-Frame-Options: DENY, and Codeman's own default-src 'self' CSP blocks cross-origin frames. Proxying dissolves all three and leaves the production CSP unchanged. The fetch happens server-side, so a tailnet-only or localhost-only dashboard is reachable from any device that can reach Codeman.

    The proxy is not an API surface: it authenticates on a 192-bit capability in the path (memory-only, rolling TTL, bound to the minting user, revoked on edit or delete) and is exempt from the cookie and Origin checks, because a sandboxed iframe is opaque-origin and sends neither. The Host allowlist is never bypassed. Iframes omit allow-same-origin unless a URL is explicitly marked trusted, and Authorization plus the session cookie are stripped upstream in both modes so CODEMAN_PASSWORD cannot leak into a dashboard. Includes an HTTP and WebSocket proxy, redirect/cookie/<base> rewriting, a runtime URL shim for requests built by dashboard JavaScript, and CORS handling for the opaque-origin frame. New endpoints under /api/webviews, storage in ~/.codeman/webviews.json, user guide in docs/web-tabs.md.

    Terminal links no longer truncate. Three separate cuts, each producing a link that opened the wrong target or none at all:

    • A single & ended the match, so every query string was cut. A WordPress edit link resolved to ?post=1479 and Claude Code's own /login URL was unusable. & is now part of a URL while && remains a boundary.
    • Links wider than the terminal were cut at the row boundary. The link provider now stitches continuation rows into one logical line and maps offsets back across rows. Handles both soft wraps (emulator, isWrapped) and hard wraps (a program wrapping its own output and emitting a newline, as Ink does), the latter being why the /login URL grew longer as the window was widened.
    • Image and PDF paths were not matched at all, so pasted-screenshot paths rendered as plain text. They now link and open the file preview, which renders images inline.

    Also fixes a pre-existing bug where .toolbar's backdrop-filter created a stacking context that trapped the Run menu's z-index, letting the welcome overlay cover it: with no session open, every item in that menu (Claude Code included) was unclickable.

codeman@1.8.1

Choose a tag to compare

@github-actions github-actions released this 27 Jul 13:07

Patch Changes

  • Mobile toolbar: a dedicated Enter button, and Shell moves into the Run dropdown.

    Submitting is a constant need on a touch keyboard, so on phones (≤430px) the toolbar slot that held "Shell" now holds a dark blue Enter button. Starting a shell, the far rarer action, moves into the expandable Run dropdown as Terminal / Shell (the Run button then reads "Run SH"). Desktop and tablet are unchanged: the green Run Shell button stays exactly where it was.

    Enter is replayed through the terminal's own input path rather than posted to the input API. This matters because local echo is on by default on touch devices: the characters you type are buffered client-side and have not yet reached the PTY, so sending a bare carriage return would submit an empty line and leave your text stranded on screen. Replaying the keypress flushes the buffered text first, then submits.

    Installer: re-runs and updates now preserve the existing network binding instead of silently reverting it, so upgrading no longer changes how the dashboard is reachable.

    Default desktop header is cleaner: the file viewer is shown by default and the plan-usage chip is unchanged, while the token-count chip and lifecycle-log button now default off. Stored preferences are still honored.

    Docs and repo housekeeping: fresh phone screenshots and a new hero GIF in both READMEs, contributor and total-commit badges, and a much shorter repo root. SECURITY.md moved to .github/ (GitHub resolves it there, so the Security policy tab is unaffected), SPEEDRUN.md to docs/, the knip config to config/, and Prettier's config into the "prettier" key of package.json. CLAUDE.md was split so the always-loaded guidance is roughly half its former size, with the deep implementation detail preserved verbatim in docs/architecture-invariants.md.

codeman@1.8.0

Choose a tag to compare

@github-actions github-actions released this 24 Jul 07:17

Minor Changes

  • Installer: choose your network binding, with LAN access as the new guided default.

    The install script now asks at the end of setup how the dashboard should be reachable:

    1. Any device on your network (0.0.0.0), the default. The installer prompts for a dashboard password (hidden input, confirmed twice); declining a password requires an explicit confirmation and the install ends with a prominent warning explaining the exposure.
    2. This machine only (127.0.0.1), the safer option for tunnel/Tailscale setups.

    The choice is wired into the generated systemd unit and launchd plist (values escaped for each format), the run-now launch path, and the printed URLs, which now include the detected LAN IP for instant phone access. Non-interactive installs keep the safe loopback default unless CODEMAN_HOST is preset, and the server binary's own default binding (127.0.0.1) is unchanged, so npm and manual installs behave exactly as before. New installer env presets: CODEMAN_HOST and CODEMAN_PASSWORD skip the prompts for automation.

codeman@1.7.1

Choose a tag to compare

@github-actions github-actions released this 23 Jul 22:47

Patch Changes

  • Mobile and UI polish plus docs refresh.
    • Mobile: the header brand collapses to a single "C" home button on phones (<430px), freeing header space for session tabs while keeping the same tap target. The compact letter lives in its own span so i18n custom branding keeps rewriting only the full wordmark.
    • UI fix: the absolutely-centered toolbar voice button no longer overlaps the case picker's chevron and "+" button. Below ~1500px (or with long case names widening the left toolbar group) it now falls back into normal flex flow where overlap is impossible; wide viewports keep the centered layout.
    • Docs: README gains a hero pitch block with deep links, npm version + GitHub stars badges, and a star CTA; CLAUDE.md core-files table synced (Infra docker modules, app.js line count); blog article images added under docs/images/blog/.

codeman@1.7.0

Choose a tag to compare

@github-actions github-actions released this 23 Jul 07:44

Minor Changes

  • Community release (thanks @shenlvkang-collab for all four PRs) plus documentation fixes.
    • fix(mobile): per-device settings now key off a stable handheld classification (MobileDetection.isHandheldDevice(): touch plus UA form-factor tokens, with User-Agent Client Hints fallback) instead of the instantaneous viewport width, so an Android foldable that unfolds past the desktop breakpoint keeps codeman-app-settings-mobile and opt-ins such as the Response Viewer and Extended Keyboard Bar. Responsive layout stays width-driven. Adds an OPPO Find N5 (unfolded) device profile and a fold/unfold/reload Playwright regression test (mobile suite now 136 devices). (#162)
    • fix(paths): SAFE_PATH_PATTERN now accepts Unicode letters and numbers (\p{L}\p{N} with the u flag), so working directories like /mnt/d/AI/中文项目 validate in Create Session, Quick Run, and Scheduled Run. All shell-metacharacter, traversal, and absolute-path protections are unchanged. (#163)
    • fix(ui): newly created run sessions render their tab immediately instead of waiting for the session:created SSE event (idempotent upsert from the POST response, with a GET /api/sessions/:id fallback for quick-start modes), and the Run button holds an in-flight lock (min 500 ms) so a double click cannot create duplicate sessions. (#164)
    • feat(ui): the synced custom display name and per-device English/Simplified Chinese UI language are described in their own entry (#165); on top of that PR, renderIndexHtml no longer recomputes windowTitle on solo-session renders, so a detached window cannot reset the push-notification hostTitle prefix to the default name.
    • docs: corrected the sse-events.ts fileoverview breakdown (148 event constants, was stale at 120; per-category counts refreshed, including Cron, Docker, Remote auto-reconnect, and Multi-user) and the CLAUDE.md SSE registry count; READMEs synced with the 1.6.2 installer behavior.

Patch Changes

  • 8d9fc41: Add a synced custom display name and a per-device English/Simplified Chinese browser UI language picker under App Settings → Display.