Repository navigation
Two-way terminal <-> dashboard links + resume button - #100
Merged
Merged
Conversation
Terminal -> Dashboard: every session gets a short link
(http://localhost:5180/s/<8-char-id>) that opens its timeline. Includes
a `karma` shell script and a `GET /sessions/resolve/{short_id}` endpoint
that also checks live-session records, so the link works even seconds
into a brand new session, before its transcript file exists yet.
Dashboard -> Terminal: the "resume" button (session card + session
detail page) now does something instead of just copying a command. If
the session is still running, it focuses that terminal. If it has
ended, it opens a new terminal, cds into the project, and runs
`claude --resume` for you.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI's Python Lint job installs unpinned latest ruff (0.16.5), which wraps multi-line lambda-with-`or` bodies in parens differently than the repo's pinned dev ruff (0.14.11). Reformatted with 0.16.5 to match CI; both versions now pass clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nstalled) Adds a "Karma Link in Your Status Line" section to SETUP.md, next to the existing Open Terminal Button docs, covering scripts/karma and a new copy-from example script for the statusline. This is documentation only, not an installer: Claude Code's statusLine is a personal, global setting shared across every project, so Karma never writes to it automatically. scripts/example-karma-statusline.py is a minimal reference — point statusLine.command at it directly if you have none, or copy the karma_segment() function into an existing custom statusline instead of overwriting it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JayantDevkar
approved these changes
Sep 18, 2026
JayantDevkar
left a comment
Owner
There was a problem hiding this comment.
Approving. The thing I cared most about here — an HTTP endpoint that spawns a terminal — holds up.
Checked and clean
- Command injection:
_build_commandshlex.quotes cwd and uuid, then_applescript_strescapes for AppleScript. Round-tripped 7 hostile paths ("; do shell script "…"; ",$(), backticks, embedded newlines) through realosascript— all came back byte-identical, payload stays inside single quotes. - The request body carries nothing: uuid is gated by
_UUID_RE, cwd comes only from on-disk session data, missing dir → 409. - Cross-origin POST → 403 via the same
_reject_cross_originguard asfocus-terminal. - Short-id resolver: regex allows no glob metacharacters; redirect has a hardcoded
/projects/prefix so no open redirect;/resolve/{short_id}isn't shadowed by/{uuid}. - Non-darwin returns
launched=Falsewith a readable message, no crash. 24/24 new tests pass locally.
Follow-ups (none blocking, leaving them here rather than inline)
api/routers/sessions.py:1927— the origin guard trusts all ofcors_origins, which includes:3000,:3001,:5173. Same guard asfocus-terminal, so not a regression, but the impact went from "raise a window" to "start a process". #101 adds a narrowerrc_trusted_origins(:5180only) for exactly this reason — once that lands, this endpoint should use the same gate.scripts/karma:40— underset -euo pipefail, a failingshort_id="$(python3 …)"kills the script before the dashboard fallback at:66(nopython3on PATH → raw "command not found"). Alsoos.path.getmtime(path)at:54sits outside thetry, so a live-session file deleted betweenglobandgetmtimeaborts the whole command.short_id="$(python3 … || true)"plus movinggetmtimeinto thetryfixes both.sessions.py:1935— dedupe keys on the original uuid's live record. Ifclaude --resumeallocates a new session id (Karma's own chain model suggests it can), the original never goes live again and each click spawns another window. Not verified — I didn't run a real resume. A short per-uuid server-side cooldown would close it cheaply.sessions.py:1885-1896— an ambiguous 8-char prefix silently resolves to the newest match. Fine today; worth returning a 409/chooser if it ever bites.frontend/src/routes/s/[short_id]/+server.ts:32— unguardedres.json()turns a non-JSON 200 into a bare 500 instead of your 502.
Not checked: Windows/Linux at runtime, the iTerm2 branch at runtime, real claude --resume semantics.
7 of 12 tasks
the-non-expert
added a commit
that referenced
this pull request
Sep 19, 2026
…n, no orphan menu Review feedback on #101, plus the rebase onto main (#99/#100/#102). - Memoize the transcript scan on (path, st_mtime_ns, st_size). The single- session GET is polled ~1s per open tab and, when RC has never been used, found no bridge_status line anywhere — so every uncached hit re-read and decoded the whole multi-MB chain. A JSONL transcript is append-only, so an unchanged transcript is now scanned exactly once. - Verify a tmux pane still owns the recorded pid before enabling the toggle. pid_is_live_claude accepts node/bun/deno, so a recycled pid in a recycled pane could have enabled RC on a *different* Claude session. The pane's pane_pid must now be that pid or an ancestor of it; unverifiable => refused. - Require Origin on the toggle, not just "trusted if present". For a keystroke-injecting endpoint, no-Origin isn't worth the benefit of the doubt: this closes DNS-rebinding and the no-Origin-same-origin-POST corner. - Drop the "Esc-dismisses" claim in read_remote_control_state — nothing here sends Esc. A stale "off" (session launched with --remote-control, no bridge_status line) meant the ON click opened the *disconnect* menu and left it open in an unfocused terminal, where the user's next Enter picks an item. Unconfirmed ON now raises the terminal like the OFF path and says so. ConversationHeader: #100's ResumeSessionButton supersedes this branch's live-session hide (it focuses the terminal when live, which is useful), so the rebase keeps #100's version outright and drops the copy-resume block. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Two related features, both about connecting a terminal to the Karma dashboard in both directions:
Terminal → Dashboard: every session gets a short link (
http://localhost:5180/s/<8-char-id>) that opens straight to that session's timeline. Akarmashell script is included so you can jump from any terminal to its Karma page with one command, andscripts/example-karma-statusline.pyis an optional copy-from reference if you want the link always visible in your status line — documented in SETUP.md, not auto-installed. Claude Code's statusLine is a personal, global setting shared across every project, so Karma never writes to it for you.Dashboard → Terminal (resume): the "resume" button on a session card, and on the session detail page, now actually does something instead of just copying a command. If the session is still running, it focuses that terminal (same as the existing focus-terminal feature). If the session has ended, it opens a new terminal, cd's into the right folder, and runs
claude --resumefor you.The link looks different depending on your terminal
Not every terminal can turn a short label into a clickable link the same way:
This isn't a downgrade I chose. Apple's Terminal.app has never implemented OSC 8, the escape-code standard that lets a terminal turn a short label into a link with a different URL hidden underneath. Terminal.app can only make a URL clickable if the URL itself is visible on screen — there's no setting or software update that changes this. Confirmed in Claude Code's own docs. Switching to iTerm2 or another modern terminal is the only way to get the short button look.
One thing to know before using this
These links only work if Karma is actually running when you click them:
apion 8020,frontendon 5180), orIf neither is true, clicking a short link (or the resume button) just shows a browser error or a blank page, because there's no server to answer. That's expected for a locally-hosted dashboard, not a bug in this PR.
Testing
🤖 Generated with Claude Code