dor CLI: argv-over-protocol + command+cwd ensure (stacked on #121)#136
Conversation
Hoist the repeated `error instanceof Error ? error.message : String(error)` coercion into a shared errorMessage() helper, and remove the requestedWorkingDirectory field from Surface which was computed but never read by any renderer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The protocol command field now carries raw string[] argv instead of a pre-quoted string. The CLI sends argv verbatim; the host (Wall) does the single shell-correct quote for the target pane's shell. This removes the double-quoting that happened when the CLI quoted for the caller shell and the host re-quoted for the target shell. Quoting moves into a node-free shell-quote.ts module so it can bundle into both the CLI and the browser webview. shell-command.ts (which pulled in node:child_process for caller-shell detection) is deleted along with the now-dead detectCallerShellKind/parentProcessName. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ensure.md snapshot rewrites the ensure semantics from --title idempotency to --cwd + live-command matching. This is aspirational and does NOT match the current code (still --title), so the cli-help snapshot test fails until the code follows. docs/specs/TODO.md is a planning scratch doc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dedupe two patterns repeated across the command runners: - renderJson(payload) replaces the `JSON.stringify(..., null, 2) + "\n"` idiom in the split, ensure, and list-surfaces renderers. - requireControlClient(options) collapses the resolve/check/new-Error boilerplate into one call returning ControlClient | Error, dropping the .value indirection at each call site. resolveControlClient is now internal to shared.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # dor/src/cli.ts # lib/src/components/Wall.tsx
The enforced-title key meant a server the human started by hand never matched the agent's `dor ensure`, so both ran their own copy. Key on the shell-integration-reported running command plus working directory instead: drop --title, add --cwd (defaults to the caller's directory, which now travels in the request since the host can't know it), and render the command rather than the title. Also reconcile the cli-expanded merge: the new iframe/kill/read/send commands used the old resolveControlClient API that this branch had replaced with requireControlClient. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the host-side surface.ensure handler to match the dor CLI's new contract: a surface matches when it is currently running the requested command in the requested cwd, instead of carrying a user-enforced title. - surfaceRunsCommand(): the match predicate — live currentCommand only, exact rawCommandLine, normalized cwd. Pure and unit-tested. - Thread --cwd through createSplitSurface so an explicit cwd (defaulting to the caller's directory) sets where the new command runs and is the cwd half of the key; drop the title path. - Seed launched commands: a `-lc`/`/c` command never loads the OSC 633 integration, so without help its pane reports nothing and ensure could not match a surface it just created. seedLaunchedCommand stamps the command run at spawn; clearing it on pty exit preserves liveness. Hand-started interactive shells already report via the injected OSC 633 integration, so they match without seeding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
createSplitSurface's title parameter is no longer passed by any caller now that ensure keys on command+cwd, so remove it along with the orphaned validateUserTitle helper and isReservedUserTitle import. Correct the callerWorkingDirectory comment, which claimed symlink resolution that neither the CLI nor the host actually performs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Trailing-slash normalization was a half-measure: path.resolve in the CLI already collapses trailing slashes (and `.`/`..`), and it never addressed the real divergences (symlinks, case). Drop sameCwdPath/normalizeCwdPath and compare cwd exactly, matching the command half. Canonicalize the default cwd through resolvePath too so what the CLI sends is always absolute, and correct the help to say symlinks are not resolved. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
52b414b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://774188fc.mouseterm.pages.dev |
| Branch Preview URL: | https://add-dor-cli.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
Reviewed the full diff — clean, cohesive expansion of the dor CLI. The argv-across-the-wire model (CLI sends raw string[], webview quotes once for the target shell via the shared shell-quote.ts) is a nice fix for the double-quoting problem, and the command+cwd ensure model is well-specified in both the spec and the help text. Tests are thorough (the surfaceRunsCommand liveness/exact-cwd cases and the seedLaunchedCommand/finishLaunchedCommandByPtyId lifecycle are exactly the edge cases I'd want covered), and CI is green.
Two non-blocking notes:
| className="block h-full w-full border-0 bg-white" | ||
| src={url} | ||
| title={api.title ?? url} | ||
| allow="autoplay; clipboard-read; clipboard-write; fullscreen; geolocation; microphone; camera" |
There was a problem hiding this comment.
Non-blocking: this grants geolocation; microphone; camera (plus clipboard) to whatever URL the iframe loads, with no sandbox attribute. For an inspection surface aimed at localhost dev servers, those device permissions seem broader than needed — a page loaded here (or one that gets XSS'd) could prompt for camera/mic/location inside the webview. Consider trimming allow to what the use case actually needs (e.g. clipboard-read; clipboard-write; fullscreen). Your call given the URL is user-typed.
There was a problem hiding this comment.
iframe is going to get removed in a future commit
There was a problem hiding this comment.
Makes sense — if the iframe is going away, the permission scope is moot. Thanks for the context.
Stacked on #121 (
cli-expanded). Review/merge #121 first; this PR's diff shows only the incremental work on top.What this adds over #121
commandfield now carries rawstring[]argv; the host quotes once via a node-freeshell-quote.ts(renamed fromshell-command.ts, shared into the browser webview), fixing double shell-quoting.ensureidempotency reworked from--titleto command + cwd matching: a surface is matched by the live shell-integration–reported command and a cwd-normalized key, so re-runningdor ensurefrom the same worktree reuses the surface and two different worktrees stay distinct (exact cwd match, no trailing-slash trimming).errorMessage, and sharerenderJson/requireControlClientacross command modules.lib/terminal-state*plumbing (command + exit-status header state).Testing
pnpm run build && node --test test/*.test.mjsindor/— 48/48 pass.🤖 Generated with Claude Code