Commit Graph
100 Commits
Author SHA1 Message Date
Codeman maintainer 5eccaca3bc Merge pull request #616 from opticon454/fix/deepseek-install-path
fix(deepseek): find pnpm when installing the terminal profile, and install it with dsh

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:51 +02:00
Codeman maintainer 16bbd260e2 Merge pull request #642 from Ark0N/fix/skill-positional-args
fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 8084cae0fe Merge pull request #628 from Randalix/fix/codex-submit-paste
fix(tmux): submit text as a bracketed paste so Codex does not swallow Enter

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer c5417abd27 Merge pull request #632 from ludovicfourrage/fix/capture-pane-sgr-reset-629
fix(tmux): reset captured SGR before live output resumes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:46 +02:00
Codeman maintainer 6e4181a2e6 Merge pull request #622 from JDProfresh/fix/attach-video-containers
fix(uploads): refuse an unsupported video before uploading it

Conflict resolved against #621 in src/web/public/image-input.js (the
_promptAttachKind doc comment only): #621's comment is kept with #622's
clause that a video MIME counts only for the containers the server
admits; #622's video MIME check and #621's document branch both stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:42 +02:00
Codeman maintainer 0866090bc3 Merge pull request #621 from JDProfresh/feat/prompt-upload-documents
feat(uploads): prompt uploads take documents

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:23 +02:00
Codeman maintainer 48637e9920 Merge pull request #623 from JDProfresh/fix/prompt-cache-report-bounds
fix(telemetry): gate the prompt cache like the model report, and keep the first 20 miss causes

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:59:23 +02:00
Codeman maintainer 688bdfb970 fix(#616): install pnpm only when none is on PATH
The DeepSeek install command asked npm for dsh and pnpm in one go. On a
machine that already has pnpm through corepack (`corepack enable` puts a
pnpm shim in node's own bin directory, which is npm's global bin for nvm
and Homebrew installs), `npm install -g pnpm` refuses to overwrite that
shim with EEXIST, and because both packages were in the same command the
whole install failed: dsh was not installed either. Measured with npm 10
against a scratch prefix holding a corepack-style shim. On master the same
machine installed dsh fine and the profile install used the existing pnpm.

The command is now `npm install -g @deepseek-ai/dsh && (command -v pnpm
>/dev/null || npm install -g pnpm)`, so pnpm is installed only when the
install environment has none, and a failed dsh install still fails the
command. The two hints that embed it follow (the launch-table drift test
requires that), and the catalogue is regenerated; install.sh withholds
the command for a launcher-only CLI, so its block is unchanged.

The test now runs the real stock string through /bin/sh with a fake npm
on a PATH it controls, for the three cases: no pnpm, an existing pnpm,
and a failing dsh install.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:37:41 +02:00
Codeman maintainer 9117b183a1 fix(#621): correct three upload doc claims
The wiki said .codeman-uploads/ is the one path "a local agent, a container
and a remote host all see the same way", two sentences after saying uploads
are refused for remote (SSH) sessions. Only a local agent and a Docker case
(workspace bind-mounted at the same absolute path) share it, which is why the
route refuses remote sessions; say that.

The same paragraph said every image goes in after a re-encode on the device,
but _normalizeImageForUpload passes a GIF through untouched to keep its
frames.

CLAUDE.md and a test comment described the image fallback as behaving
"as before 1.36", a release that never existed (1.35.0 was followed by
1.40.0). It means the image path as it was before this change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:23:28 +02:00
Codeman maintainer b6218c1490 docs(readme): link the Codeman Skill wiki page from both READMEs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 00:46:29 +02:00
Codeman maintainer 4eef417da6 fix(skill): keep the §0 bootstrap intact when Claude Code loads the skill with arguments
Claude Code rewrites a skill body that it loads with arguments: every bare
$1, $2 ... becomes the matching word of the request (0-based). Nine lines of
the agent preamble used bare positional parameters, so an agent that pasted
the §0 bootstrap (every session without a pre-seeded preamble: Agent Skill
injection off, the default, or a remote or Docker session) wrote helpers
that queried session "5" with timeout=claude and looked up "5=" instead of
CODEMAN_PASSWORD= in the .env, sending every call unauthenticated.

- $1/$2 -> ${1}/${2} in preamble.sh and the identical §0 heredoc in
  SKILL.md; same bash, and Claude Code's pattern (\$\d+(?!\w)) skips it.
- CODEMAN_PREAMBLE 1.33.4 -> 1.41.2, so a cache file written by the broken
  bootstrap (which carries the old stamp) is rewritten on the next run.
- A comment at the top of the preamble states the rule.
- New test: Claude Code 2.1.300's substitution applied to SKILL.md with
  sample arguments must leave the body, and the pasted heredoc, unchanged.
  It fails on the old file with the exact sessions/5 and timeout=claude lines.
- plugins/codeman re-synced; patch changeset.

Verified with real Claude Code 2.1.300 (the delivered heredoc is now
byte-identical to preamble.sh; before, 9 lines differed) and by running the
pasted helpers against a local recording server.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 19:33:09 +02:00
Codeman maintainer df2bdc8a9e docs(wiki): add a Codeman Skill page with the skill GIFs
The skill only had a section inside "Driving Codeman From An Agent". It now
has its own page, docs/wiki/Codeman-Skill.md, built around three real runs:

- five Claude Code workers powering on in the tile grid (2026-10-15)
- eight worker tabs with lineage lines back to the lead (2026-10-15)
- a DeepSeek Harness worker and a Claude Code worker from one prompt
  (2026-10-10)

plus every install route, prompts that work as written, a run start to
finish, which workers it can drive, what it will and will not do on its
own, the Codex notes and what is in the skill. The page says that the grid
recording put each worker on the grid as it started, since sessions an
agent starts do not join the grid by themselves.

The agent page keeps a short skill section (the heading anchor stays) and
links to the new page; the sidebar, Home, FAQ and Settings Reference link
to it too. The FAQ answer no longer says the skill is enabled per case only.
docs/media-assets.md records the new embeds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 19:10:38 +02:00
Codeman maintainer 11e8ac0b2d docs(readme): add two codeman skill GIFs (5 tiles, 8 tabs with lineage)
Two live captures of a Claude Code lead driving the codeman skill, added to
the agent skill section of both READMEs:

- Step 2: `use codeman to start 5 claude workers, one per TODO item`, five
  Claude Code workers power on as tiles in the grid (1120x630, 17 s, 10 MB).
- Step 3: the same prompt with 8 workers in the normal view, eight tabs
  power on with lineage lines back to the lead, then a click into a live
  worker (1120x630, 16 s, 8 MB).

Real sessions only, cut from the same raw takes as the promo videos; the
time-lapse stretches are labelled on screen. CRT beats play at 50 fps, the
busy time-lapses at 25 fps, one global palette, no lossy pass. Privacy OCR
on every second frame: clean. docs/media-assets.md lists both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 18:17:34 +02:00
Codeman maintainer a8a8bb71a3 docs(media): keep video off GitHub, document where media lives
Only GIFs that a public page embeds stay in docs/images/: the five README
GIFs plus subagent-demo-20260724.gif, which the published "mission control"
article hotlinks from master. Seven GIFs nothing embeds leave the tree
(their masters are kept in the maintainer's local media library).

.gitignore now blocks *.mp4/*.webm/*.mov/*.m4v/*.mkv/*.avi/*.gif outside
docs/images/*.gif (and the promo branch's Remotion inputs), so a video can
only be committed with an explicit `git add -f`.

docs/media-assets.md records the rule: what is tracked and where it is
embedded, website media (pr/website/assets, deployed separately), release
clips on the design-assets branch, and the local library layout.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 00:10:06 +02:00
Codeman maintainer 1a692f588e docs(readme.zh-CN): translate the remaining English-only sections
The installer's three up-front questions, the :443 fallback, the
optional tailnet rename, command-line flags and install.sh status; the
installer's closing QR note in the Tailscale tip; Diagnostics in
Settings, the bottom-bar git status and creating a case in a custom
folder; docker load in the Docker export bullet; and the installer
note on the loopback default. The Chinese README now carries the same
content as the English one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:46:05 +02:00
Codeman maintainer b68b098d1c docs(readme.zh-CN): catch the Chinese README up to 1.41.0 and 1.41.1
Mirrors the English README update: tile grid ranking, saved layouts,
tile animations, wheel scrolling in tiles, Detach Tiles and gesture
moves; rail search, tab groups and Focus; HTML pages, the folder grid
and clickable ~/ and relative paths; .codeman-uploads and the phone
Attach button; OSC 52, the prompt-cache readout, MCP server sync with
Copilot CLI, Settings Apply and notification display times; remote SSH
scrollback; the sandboxed HTML page view in Security; Ctrl+W and the
tile shortcuts in the shortcut table.

Adds what the Chinese README never had: the `codeman agent` and
`codeman mcp` sections, the agent skill for every CLI (install table,
--mode, the Codeman Agent Skill setting, the skill GIF), and the
refreshed dashboard tour and tab-states images.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:46:05 +02:00
Codeman maintainer 03e6a159b1 docs(readme): catch the README up to 1.41.0 and 1.41.1
Tile grid (session ranking, saved layouts, tile animations, wheel
scrolling in tiles, Detach Tiles, gesture moves), rail search, tab
groups and Focus, HTML pages and the folder grid, clickable ~/ and
relative paths, .codeman-uploads and the phone Attach button with
videos, OSC 52, the prompt-cache readout, MCP server sync with Copilot
CLI, Settings Apply, notification display times and remote SSH
scrollback.

Also corrects three stale claims: HTML now renders in the sandboxed
page view, Ctrl+W has not closed a session since 1.40.0, and the tile
shortcuts were missing from the table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:46:05 +02:00
Codeman maintainer 29f76e7ec3 docs: split CLAUDE.md into root, src/web/CLAUDE.md and docs/releasing.md
The root CLAUDE.md had grown to 202 KB, all of it loaded into every
session. This lands the split drafted on 2026-10-10 (it sat uncommitted
in the shared checkout through two releases), redone on top of today's
master so nothing that landed since is lost:

- src/web/CLAUDE.md: the frontend invariants (every src/web/public/ UI
  subsystem, the module notes, the XLSX preview limits, the z-index
  stack). Claude Code loads it when a file under src/web/ is read; the
  root file tells sessions to Read it before editing src/web/public/.
  Its link targets are relative to the file so they resolve on GitHub.
- docs/releasing.md: the COM procedure (now nine steps, with the MCP
  registry publish), behind a pointer in the root COM section. The
  Version line stays in the root file.
- Root CLAUDE.md (130 KB): everything else, plus the split's trims: the
  Core Files table, the reference-only rows of the Additional Commands
  table, Data Flow and the respawn presets, whose rules already live in
  the root's other paragraphs (the "Read before touching a subsystem"
  line keeps the table's doc pointers).

Every other paragraph of master's CLAUDE.md lands in exactly one of the
three files, checked line by line; the paragraphs master changed since
the draft (tile grid, OSC 52, prompt uploads, file-path links and the
HTML view, markdown preview, z-index, MCP server, agent CLI and others)
are in their current form. Both new files join CLAUDE.md in
.prettierignore. Four code comments and docs/architecture-invariants.md
now point at src/web/CLAUDE.md for the moved sections.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:43:45 +02:00
Codeman maintainer bc2d42c2c8 docs(release): publish each release to the MCP registry (COM step 8)
`codeman mcp` is listed in the official MCP registry as
io.github.Ark0N/codeman (first published for 1.41.1). The registry does
not follow npm, so without a step in the COM procedure the listing stays
on the version it was last published with. The step also records the
two traps hit on the first publish: the Snap Store mcp-publisher (1.1.0)
rejects the current server.json schema with a false "deprecated schema
detected", and older publishers save live login tokens into the current
directory as .mcpregistry_* files.

The announcement step becomes step 9.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:40:33 +02:00
Codeman maintainer af8d0ef69c docs(readme): use the 1600px hero GIF at 800px display width
The 800px GIF rendered the header stats strip (CPU, MEM, 5H/7D) at ~5px
text height. The 1600px source shown at width=800 stays sharp on high-DPI
screens. New filename so GitHub's image cache serves the new file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 20:09:20 +02:00
Codeman maintainer 7eaa39a67e chore: version packages (1.41.1)
One consolidated patch changeset for the 24 PRs in this release (the PRs'
own changesets folded in), CHANGELOG, package and plugin manifest versions,
and the CLAUDE.md version line.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 19:22:13 +02:00
Codeman maintainer 9faf567d41 Merge pull request #610: fix(approvals): treat DeepSeek permission prompts as terminal-only alerts (author's Prettier follow-up) 2026-10-13 19:22:07 +02:00
Codeman maintainer 91750d4bd5 docs(tiles): Detach Tiles known limits (#584 merge-time fix)
One stored grid per browser shared by every Codeman window, no cross-profile
notice (BroadcastChannel is per profile), and the user-activation window that
blocks a pop-out from a drag-out held too long.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:59:56 +02:00
Codeman maintainer 63ac10dcc0 fix(tiles): docking a tile asks any pop-out of that session to close (#584 merge-time fix)
_dockForeignTile posted close-request only when this window knew the session
was popped out. A pop-out opened from another dashboard window was not in this
window's detachedSessions, so it stayed open beside the newly docked tile and
two views sized one PTY. The request now goes out for every docked id;
dashboards ignore it and only that session's pop-out reacts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:59:39 +02:00
Codeman maintainer 48e91fc1d9 fix(tiles): a claim that lands mid-drag lets the dragged tile go (#584 merge-time fix)
_onTileAdoptedElsewhere acted only on a tear-off, a pending tear-off or a drag
that just ended. A header drag carries the tile id even when this window has
Detach Tiles off (no tear-off), so another window could dock the tile and its
claim could arrive before this drag ended; the tile then stayed here as well.
The header drag (_draggedTileId) now counts as dragging too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:59:24 +02:00
Codeman maintainer 3975089af3 fix(tiles): a tile docked in another window leaves this window's stored grid alone (#584 merge-time fix)
There is one stored grid per browser (codeman:tile-grid), and the window that
docked the tile has just written it. When this window then hears the claim and
drops the tile, removeTile (or, for its last tile, closeTileGrid with
keepStored: false) overwrote or forgot that stored grid. The removal now runs
under _tilePersistHold, and the last-tile close keeps the stored grid.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:58:22 +02:00
Codeman maintainer 9ad43fbd88 Merge pull request #584: feat(tiles): move tiles by hand, and detach a tile into its own window 2026-10-13 18:57:50 +02:00
Codeman maintainer 5e83cd3885 fix(html-view): a ~/ HTML link renders as a page too (#547/#604 integration)
#604 makes `~/` links clickable and passes them through unchanged, since only
the server knows the session host's home. The html-view mint read `~/x.html`
as workspace-relative and answered 404, so the preview fell back to the
source view. It now expands `~/` against this host's home, as the attachment
route does, and refuses it in a Docker case (that `~` is the container's)
with the same message as the attachment and folder routes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:56:58 +02:00
Codeman maintainer 1172ef56f8 test(file-preview): every link form routes .md and .html to the preview (#547/#604 integration)
Pins the merge resolution: `~/`, relative (`./`, bare) and hard-wrapped
links to .md/.markdown and .html/.htm open the file preview (rendered
markdown, HTML page) exactly like rooted ones, while other text such as a
relative .json keeps the log viewer. Fails if the terminal's activate path
drops rendersAsPage().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:56:58 +02:00
Codeman maintainer 0996f99fbb Merge branch 'rel1411/html-md': render HTML files as pages (#547) and open .md from the terminal in the rendered viewer
Conflicts resolved against #604, #606 and #601:
- constants.js: the rooted link pattern takes html-md's `markdown` and
  `html?` with #606's m4v/ogv; #604's relative pattern gets the same list
  (link-provider-regex pins the two equal); #604's findFilePathLinks /
  folder helpers and html-md's rendersAsPage() are both kept, and
  window.CodemanFilePaths exports all of them.
- terminal-ui.js: #604's resolved `target` (relative, ~/ and folder links)
  now also goes through rendersAsPage(), so every link kind routes .html to
  the page preview; .md routes there through FILE_PREVIEW_EXTENSIONS.
- CLAUDE.md, api-reference, architecture-invariants: both sides' text kept.
- test/folder-grid-ui.test.ts (#604) stubs rendersAsPage() like the other
  panels-ui harnesses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:55:02 +02:00
Codeman maintainer b15033259a style(approvals): prettier on the two lines #610 wrapped (#610 merge-time fix)
format:check failed on src/web/server.ts and src/web/public/approvals-ui.js;
both assignments fit on one line at printWidth 120.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:29:09 +02:00
Codeman maintainer 0feb5658ca Merge pull request #610: fix(approvals): treat DeepSeek permission prompts as terminal-only alerts 2026-10-13 18:29:01 +02:00
Codeman maintainer 82d588a339 docs(mcp-sync): the alt config file is written when it parses (#599 merge-time fix)
Three comments said `altPath` is never written, but run() makes the alt file
the write target when the primary is absent. The behaviour is fine (a JSONC
file with comments fails the parse guard and is never written); the
comments now say so, so nobody loosens the path rule on that belief.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:28:34 +02:00
Codeman maintainer 1f41eacd87 fix(mcp-sync): read mcp.servers as a V1 server when it has a V1 type (#599 merge-time fix)
mcpTable took any record at `mcp.servers` as OpenCode's V2 container.
OpenCode itself does so only when the value is not a V1 server (`type`
local or remote). So a server named "servers" copied from another CLI was
written as V1, re-parsed as a container, failed the read-back guard and
failed the whole OpenCode target, and an existing V1 "servers" stopped
being read. The V2 entry now also wins a name collision, as in OpenCode;
sync never writes a name already defined, so the read-back is unaffected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:28:15 +02:00
Codeman maintainer a8b8530722 Merge pull request #599: fix(mcp-sync): read Gemini bare url as http, OpenCode V2 mcp.servers, and opencode.jsonc 2026-10-13 18:27:53 +02:00
Codeman maintainer c3cee40f56 fix(mobile): a pasted ESC[201~ cannot end the bracketed paste early (#600 merge-time fix)
_sendBracketedPaste wrapped the text as is, so clipboard text carrying the
end marker (a pastejacking page can write one) closed the bracket early and
the rest was typed as keys, its CR running a command before review. The
paste markers inside the text are now dropped first; this also hardens the
Compose path, which shares the helper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:27:39 +02:00
Codeman maintainer b7c18ab5b7 Merge pull request #600: fix(mobile): shell Paste inserts a multi-line block as one bracketed paste 2026-10-13 18:27:11 +02:00
Codeman maintainer 70e03b8ae7 fix(preview): relative path links take m4v and ogv like rooted ones (#604/#606 integration)
#606 added m4v and ogv to the rooted file-path link pattern for the new video
uploads; #604's relative path pattern copies that extension list, and its
test pins the two equal. The relative pattern now carries them too, so a
relative `clip.m4v` links like `/home/u/clip.m4v`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:26:46 +02:00
Codeman maintainer de06cb12d1 fix(i18n): drop the duplicate zh-CN Attach key (#606 merge-time fix)
ZH_CN already had `Attach: '附加'` (the tile grid's and remote discovery's
Attach buttons). #606 added a second `Attach: '添加附件'` later in the same
object literal, which won and turned every Attach into "add attachment".
The duplicate is removed; 附加 covers the phone toolbar button too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:25:49 +02:00
Codeman maintainer eaa9044520 fix(uploads): drain a refused video body before answering (#606 merge-time fix)
The ?kind=video branch called req.file(), which pipes the request into
busboy, then answered 400 (unsupported container) or 403 (upload dir not a
regular directory) without reading part.file. The server then stopped
reading the body, and a browser fetch, which reads the response only after
its upload is sent, hung on "Uploading 1/1 file" for an Android 3gp or an
.mkv. Both early answers now consume the part first, as the mislabeled
container path already did. (Reviewer-verified in Chromium: hang before,
400 in about 0.2 s after; inject and a Node client do not reproduce the hang.)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:25:07 +02:00
Codeman maintainer 6c7d63d45e Merge pull request #606: feat(mobile): phone Attach button and camera-roll video uploads
Conflicts resolved against #605 (and #602's test mock):
- CLAUDE.md Prompt uploads: #606's rewrite is kept and #605's bounded
  workspace probe sentence follows the remote (SSH) refusal.
- test/routes/session-routes.test.ts: the remote-pane-history and
  bounded-path-probe mocks are kept beside #606's _resetPasteRateBuckets
  import, and both PRs' paste-image tests are kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:21:52 +02:00
Codeman maintainer 6bbfc44054 Merge pull request #605: fix(uploads): probe the workspace bounded before a prompt upload
Conflict resolved in test/routes/session-routes.test.ts: #602's
remote-pane-history mock and #605's bounded-path-probe mock are both kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:20:21 +02:00
Codeman maintainer 3d341f46a4 fix(i18n): zh-CN for the working folder row and its copy toasts (#593 merge-time fix)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:19:17 +02:00
Codeman maintainer f0b762e745 fix(session-options): label a remote or Docker session's working folder correctly (#593 merge-time fix)
The row said the path is "on the machine hosting Codeman" for every session.
A remote SSH session's workingDir is the path on the remote host, and a
Docker session's CLI runs inside the container the host folder is mounted
into. openSessionOptions now sets the description from session.remote /
session.docker (the id it targets was added with the layout fix).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:19:17 +02:00
Codeman maintainer 771c23e261 fix(session-options): stack the working folder row so long paths fit (#593 merge-time fix)
The row was a plain flex `.set-row` with a non-shrinking actions column, so
the <code> took its full max-content width: the label column collapsed to
0px, the path overlapped the description and the Copy button left the row
(off-screen on a phone). It now uses the stacked `set-row-block` layout the
Session color row already uses, and the path shrinks and wraps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:19:06 +02:00
Codeman maintainer 0ff9b798af Merge pull request #593: feat(session-options): show the session's working folder with a copy button 2026-10-13 18:19:01 +02:00
Codeman maintainer 33c6cf5d20 chore(gesture): revert the stray gesture bundle path comment (#591 merge-time fix)
#591 rebuilt the committed gesture bundle in a worktree whose node_modules sat
in a sibling directory, so its esbuild source comment named that local path.
Nothing in the PR touches the gesture source; the bundle goes back to master's
bytes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:18:22 +02:00
Codeman maintainer 9b9752d997 Merge pull request #591: fix(sessions): restore a session's colour, pin and image watcher after a restart 2026-10-13 18:18:22 +02:00
Codeman maintainer 60fd71470c Merge pull request #590: fix(cases): show the real reason when Link Existing rejects a case 2026-10-13 18:17:49 +02:00
Codeman maintainer 836402c2c4 fix(terminal): a replay strips the 8-bit C1 form of OSC 52 too (#611 merge-time fix)
xterm.js still parses OSC 52 introduced by C1 OSC (\x9d) and ended by C1 ST
(\x9c). OSC52_STRIP_RE matched only the 7-bit forms, so a direct-PTY session
that emitted the C1 form re-copied old text on every replay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:17:18 +02:00
Codeman maintainer f073742cf7 fix(terminal): an OSC 52 copy hands focus back to the terminal (#611 merge-time fix)
When navigator.clipboard is missing (plain HTTP, install.sh's LAN option) or
refuses the write (an unfocused window), _copyText falls back to a hidden
textarea whose select() takes focus, and removing it left focus on <body>:
the next keystrokes after Claude Code's /copy went nowhere. The handler now
restores the element focused before the copy, as Auto Copy does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:16:56 +02:00
Codeman maintainer f3d7a86c16 Merge pull request #611: feat(terminal): copy OSC 52 clipboard writes from the PTY into the browser clipboard 2026-10-13 18:16:20 +02:00
Codeman maintainer 34b867025d fix(tabs): Escape from a flat-rail web-tab menu returns focus to the row (#582 merge-time fix)
#582 made the web-tab actions menu reachable on the flat rail (ContextMenu or
Shift+F10 on a role=tab row), but closeTabGroupMenu only looked for a tree
item or a Focus shortcut, so Escape dropped focus to <body>. A `.session-tab`
trigger now counts too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:15:21 +02:00
Codeman maintainer 5d31e42db0 fix(tabs): Follow parent again keeps a Focus pin (#528/#582 integration)
#528's browser followParent operation rebuilt the target ref as
{ kind, id } to drop its manual placement, which also dropped #582's
`focus` flag. The browser PUTs whole layouts, so the pin was erased on the
next save. The operation now copies the ref and removes only `placement`.
#528's row-menu test also expects #582's 'Add to Focus' entry now.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:15:02 +02:00
Codeman maintainer a6ce52d3e7 Merge pull request #582: feat(tabs): pin tabs to a Focus section on the vertical rail
Conflicts resolved against #528:
- CLAUDE.md Owner tab layouts: #582's Focus sentences and #528's
  tabGroupId / spawnedTabsFollowParent sentences are both kept.
- i18n.js: both features' ZH_CN entries kept.
- tab-layout-browser.js: the section-model JSDoc documents #528's
  `options.parents` and #582's `focusRefs` return field.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:13:03 +02:00
Codeman maintainer 5ac27f3eec fix(tabs): children spawned from one parent keep creation order (#528 merge-time fix)
With the follow setting off (the default), pinCreatedChild marked each new
child manual where normalization put it: right after the parent's FOLLOWING
block, which no longer holds the siblings pinned before it. A fan-out
therefore showed parent, w3, w2, w1 in the layout and the global
sessionOrder. The pinned child now steps past the siblings (and their
descendants) already after the parent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:10:49 +02:00
Codeman maintainer a38eab49cd Merge pull request #528: feat(tabs): place new, child and web tabs into groups
Conflict resolved in docs/api-reference.md: #607's prompt cache section and
#528's tabGroupId section are both kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:10:37 +02:00
Codeman maintainer 9c56f7823d fix(preview): a shell prompt's trailing $ or # stays out of its folder link (#604 merge-time fix)
The folder link pattern treats `:` as a boundary and allows `$`/`#` in a path,
so every default bash prompt (`user@host:~/proj$`, `root@c:/workspace#`)
became a folder link to a path that does not exist, and clicking it showed
"Folder not found". folderLinkPath now strips them, so the link opens the
prompt's cwd.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:09:08 +02:00
Codeman maintainer 076f62fa1b fix(preview): refuse a bare ~ folder in a Docker case too (#604 merge-time fix)
The Docker refusal on POST /api/sessions/:id/folders tested only `~/`, but
listFolder also expands a bare `~` to this host's home, so a Docker case
could list and serve the host home through it (the preview's "Show folder"
for a `~/x.png` link reached it with no typing). A bare `~` now gets the same
400.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:08:38 +02:00
Codeman maintainer 90034d07d7 Merge pull request #604: feat(preview): open ~/, relative and hard-wrapped paths; folder grid 2026-10-13 18:08:12 +02:00
Codeman maintainer 887ee7fe64 docs(telemetry): expected_rebuilds is no longer the compaction signal (#607 merge-time fix)
Two comments still described the abandoned expected_rebuilds rise rule;
setPromptCache no longer reads that field for the compaction stamp.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:07:26 +02:00
Codeman maintainer 62227145dc Merge pull request #607: feat(telemetry): read out Claude Code's prompt cache state per session 2026-10-13 18:07:15 +02:00
Codeman maintainer 2e65daff83 style(terminal): re-wrap the _maybePageCliTranscript JSDoc to 120 columns (#527 merge-time fix)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:06:35 +02:00
Codeman maintainer 0e11012470 Merge pull request #527: fix(terminal): page a hollow Codex transcript with PageUp/PageDown from the first wheel event
Conflict resolved in terminal-tile.js's @dependency header: #601's
SHELL_LOAD_BASE_QUERY/SHELL_LOAD_QUERY and #527's pageKeysForGesture and
app.tabAlerts are all listed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:06:24 +02:00
Codeman maintainer 5b8313988f fix(terminal): no paneHistoryLines for a remote session (#583/#602 integration)
A remote session's local pane hosts the remote tmux client in the alternate
screen, so its history_size is always 0. #583 read that as "nothing to load"
and hid the partial-history notice, but with #602 a full=1 load reads the real
scrollback from the remote tmux. The route now leaves the field out for remote
sessions, so their notice keeps the byte-based wording and its Load full
history button.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:05:30 +02:00
Codeman maintainer 628b6e5832 docs(remote): a frame that starts on a wrapped line's second half is not placed (#609 merge-time fix)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:04:36 +02:00
Codeman maintainer aca358126d fix(remote): reset SGR where the remote history meets the remote screen rows (#609 merge-time fix)
#609 keeps remote screen rows between the remote history and the local
frame. Those screen rows come from a separate capture-pane in the remote
command list, encoded from the default state, so the history's last row could
leave a background or dim active over them. The history now ends with a
reset before the screen rows, as the rows above the frame already do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:04:29 +02:00
Codeman maintainer 239f15437b Merge pull request #609: fix(remote): close the row gaps at the join in a remote history load
Conflict resolved in tmux-manager.ts: #609's screen-aware
joinScrollbackAndFrame keeps #602's merge-time SGR reset before the frame,
and #609's join tests expect it. The #601/#602 route test now mocks the
remote read in #609's { scrollback, screen } shape.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:04:05 +02:00
Codeman maintainer f78b820302 fix(terminal): a remote shell load keeps the remote scrollback (#601/#602 integration)
A remote session's local pane hosts the remote tmux client, so it is always
in the alternate screen. #601's alt=recording fallback therefore threw away
the scrollback #602 had just spliced in from the remote tmux on every remote
shell load. The fallback now applies only when no remote scrollback was
read (the read failed, or the session is not remote).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:02:36 +02:00
Codeman maintainer d571c41fb1 fix(remote): reset SGR at the remote scrollback splice seam (#602 merge-time fix)
joinScrollbackAndFrame joins two separate `capture-pane -e` outputs. The
remote history's last row can end with attributes still active (a diff row's
background, dim tool output), while the local frame is encoded relative to
the default state, so its first plain rows inherited them in the browser. A
`\x1b[0m` now ends the scrollback before the frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:01:45 +02:00
Codeman maintainer f9f00bdb5e Merge pull request #602: fix(remote): load a remote session's scrollback from the remote tmux
A full-history load of a remote SSH session reads the scrollback from the
remote tmux in one bounded ssh round trip and splices it above the local
frame. A transport failure (ssh 255, timeout, unparseable output) starts a
30 s per-host back-off; a missing remote session is per-session only.

Conflict resolved in session-routes.ts: #602's remote scrollback read runs
before #601's capture helper and its alternate-screen fallback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 18:01:15 +02:00
Codeman maintainer 78ed6bf004 docs(terminal): only a full=1 pass without alt=recording is never mux-visible (#601 merge-time fix)
The geometry-retry comment in selectSession and the #435 invariant said the
route never answers full=1 with mux-visible; a shell load's alt=recording on
an alternate-screen pane now is. The conclusion holds (a useFullHistory pass
never sends it), so only the stated reason changes. Also lists
SHELL_LOAD_BASE_QUERY in terminal-tile.js's @dependency header.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:59:20 +02:00
Codeman maintainer 36f8afdaba Merge pull request #601: fix(terminal): load a shell from tmux's rendered history, not the byte recording
Conflicts resolved against #583 and #588:
- One history-depth field: #583's `historyLines` / `capturedHistoryLines`
  (reported as `paneHistoryLines`) is kept, and #601's lines cut now reads it
  instead of a second `capturedHistorySize`. #601's `alternateOn` /
  `capturedAlternateOn` joins it, and the one display-message format string
  ends `#{history_size} #{alternate_on}`.
- selectSession keeps #588's full=1 switch for panes with no scrollback, gated
  off remote and Docker sessions, and #601's SHELL_LOAD_QUERY for shells.
- computeHistoryTruncationNotice names a line count when paneHistoryLines is
  known (#583) and otherwise uses #601's remainder wording.
- docs: api-reference lists both historyLinesCut and paneHistoryLines; the
  notice invariant keys on history_size while the shell's automatic load is
  the one place alternate_on decides.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:59:07 +02:00
Codeman maintainer 1855d9423b fix(terminal): remote and Docker tabs keep the byte tail on a switch (#588 merge-time fix)
A remote (ssh) or Docker session's local pane hosts a nested tmux client in
the alternate screen, so it always reports paneHistoryLines 0 while its byte
tail carries the real scrolled-off output. The new full=1 switch therefore
replaced their scrollback with the visible frame on every revisit. Those
sessions now keep the tail; the pinned test string and the invariants note
say so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:49:43 +02:00
Codeman maintainer 7ba1e51c51 Merge pull request #588: perf(terminal): tab switch to a fullscreen Claude tab loads only its current screen
Conflict resolved in config/test-suites.ts: both new browser test globs kept.
2026-10-13 17:49:16 +02:00
Codeman maintainer b4c87360d3 docs(api): paneHistoryLines is absent when the pane geometry was not read (#583 merge-time fix)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:48:21 +02:00
Codeman maintainer ee038ad83c fix(terminal): the history notice waits for the pull already in flight (#583 merge-time fix)
_smoothScrollBy and the touch paths call _maybeLoadMoreHistoryOnScroll on
every frame. Once the viewport sat at row 0, the next frame hit the real
pull's in-flight early return, so its reveal ran at once and the notice
showed the pre-pull tail state for the whole pull. A later frame now leaves
the reveal to the call that started the pull.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:48:11 +02:00
Codeman maintainer f2d5af2b4c Merge pull request #583: fix(terminal): partial-history notice only when there is history to load 2026-10-13 17:47:37 +02:00
Codeman maintainer dedf56e24b fix(agent-cli): httpRequest rejects when the connection drops mid-body (#615 merge-time fix)
The response listened for 'data' and 'end' only. A socket closed after the
headers emits 'error' on the response and never 'end', so the promise never
settled. A one-shot `codeman agent` just exits, but the long-lived
`codeman mcp` left that tool call unanswered forever.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:47:09 +02:00
Codeman maintainer 71647e090d fix(mcp): start_session returns the worker's id when only the readiness call fails (#615 merge-time fix)
After quick-start succeeded, a failed wait-output readiness call (a 429 from
the waiter cap, a network blip) became an isError result without the session
id, so the model could neither prompt nor delete the worker it had just
started, and a retry started a second one. It now returns the id with
ready: null and a note, as `codeman agent spawn` does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:46:08 +02:00
Codeman maintainer 35e21dd676 fix(mcp): keep the input seq strictly increasing within one process (#615 merge-time fix)
send_prompt and interrupt_session tagged input with seq = Date.now() under
one clientId. The server drops a seq at or below the last one it saw for that
clientId and still answers 200, so two inputs in one millisecond (parallel
tool calls) or a clock stepped back lost the second input while the tool
reported it sent. The seq is now max(last + 1, now).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:45:21 +02:00
Codeman maintainer 3072449c94 docs(mcp): forward CODEMAN_MUX and CODEMAN_SESSION_ID in the Codex config (#615 merge-time fix)
Codex starts stdio MCP servers with a minimal environment, so `codeman mcp`
under the documented Codex config never learned the session it runs in: the
self-protection and the children rule did not apply. Both Codex TOML snippets
now forward the two variables with `env_vars`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:44:25 +02:00
Codeman maintainer bade1f1966 Merge pull request #615: feat(mcp): codeman mcp, a Model Context Protocol server for any MCP client 2026-10-13 17:44:15 +02:00
Codeman maintainer 5205649889 docs(claude-md): the agent preamble is seeded for every CLI that takes the skill (#613 merge-time fix)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:44:07 +02:00
Codeman maintainer e65b4f35e6 fix(cli): skill uninstall --case skips a symlinked skills dir instead of exiting 1 (#613 merge-time fix)
A shared-skills layout (`.agents/skills -> ../.claude/skills`) made
`codeman skill uninstall --case` report the symlink refusal as a failure and
exit 1 on every run, even with nothing left to remove. The refusal itself is
kept (nothing is written through the link); it is now reported as a skipped
directory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:44:07 +02:00
Codeman maintainer 50a1b58d2b Merge pull request #613: Give the codeman agent skill to every agent CLI, not just Claude Code 2026-10-13 17:42:58 +02:00
Codeman maintainer 9e8a851a2c fix(spreadsheet-preview): filter and index a shared worksheet rels list once (#612 merge-time fix)
ExcelJS's worksheet pattern is unanchored, so several parsed worksheets can
share one sheetNo and therefore one rels list. repairExcelJsModel filtered and
indexed that list once per worksheet, which let a crafted admitted file run
past the 20 s preview timeout. Each distinct list is now filtered and indexed
once per sheetNo, and the relationship Type strings are built once.
SPREADSHEET_ASSET_VERSION is refreshed for the edited core.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:42:52 +02:00
Codeman maintainer 03a9259ae1 Merge pull request #612: fix(spreadsheet-preview): open openpyxl workbooks that have a comment, table or chart 2026-10-13 17:41:13 +02:00
Codeman maintainer ba5649bcd4 feat(html-view): open network access for rendered pages (owner decision)
Owner decision 2026-10-13 (#547): a rendered .html page behaves like a
file opened from disk in a browser. It may load scripts, styles, fonts,
images and media from any host and make fetch, XHR, WebSocket, beacon
and form requests to any host.

The /html-view Content-Security-Policy is therefore the `sandbox`
directive alone (HTML_VIEW_CSP, now exported and documented): the same
sandbox flags as before, never `allow-same-origin`, and no fetch
directive. It replaces Codeman's own `default-src 'self'` policy on these
responses. Everything that confines a page stays: the opaque origin (no
Codeman cookies or document, API writes with `Origin: null` refused, API
reads not readable cross-origin), and the capability serving only the
page's own folder under every path, symlink, dot, extension and
confinement rule.

A new test builds the route behind registerSecurityHeaders, as in
production, and pins the exact header: one `sandbox` directive with the
current flags, no `allow-same-origin`, no `-src` directive. The security
architecture, architecture invariants, API reference, CLAUDE.md and the
Working With Files wiki page now say what a page can do (reach the
network, read its own folder) and cannot do (Codeman's origin, cookies,
API, other folders), and that this is the owner's decision.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:39:36 +02:00
Codeman maintainer c1fde8e639 test(file-preview): give the stubbed harnesses rendersAsPage()
Three suites load panels-ui.js without constants.js and stub the globals
it uses (escapeHtml, CodemanBase). openFilePreview() now asks
rendersAsPage() from constants.js, so those stubs need it too; none of
their files is an HTML page, so it answers false.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:23:25 +02:00
Codeman maintainer 1c19641280 style(html-view): prettier-clean the resolved-path dot check
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:20:18 +02:00
Codeman maintainer 79b9181932 feat(file-preview): open markdown clicked in the terminal in the rendered viewer
An in-workspace .md path clicked in the terminal went to the log viewer
(tail -f, a "streaming" header over raw text), because md was kept out
of FILE_PREVIEW_EXTENSIONS on purpose; every other surface already
rendered it. Owner decision 2026-10-13: markdown is a document to read,
so md and markdown join FILE_PREVIEW_EXTENSIONS and a click or tap opens
the file preview, rendered by default with the MD pill for source. The
main pane, every grid tile and the split's Pane B share the link
provider, so all of them route the same way. Other in-workspace text
(.log, .txt, .json, code) keeps the log viewer and its live follow.

FILE_PATH_LINK_PATTERN now also links `.markdown` and `.htm`, which
were not clickable at all.

Tests: file-preview-markdown.test.ts now asserts md and markdown ARE
routed to the preview (and plain text is not); terminal-touch-tap pins a
tapped .md, .markdown and .html opening the preview while .log, .txt and
.json open the log viewer, and a tile-registered provider routing the
same way against the tile's own session. Docs: CLAUDE.md, the
architecture invariants and the wiki (Working With Files, Mobile Guide).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:19:02 +02:00
Codeman maintainer 0168444fda docs(api): document the rendered-HTML routes
Adds GET /html-view/:cap/* to the list of endpoints exempt from the JSON
envelope, and a "Rendered HTML files" section covering the mint
(POST /api/v1/sessions/:id/html-view: body, response, capability
lifetime, every refusal and its status) and the capability-gated serving
route (what it serves, the sandbox CSP and other headers, Range, the
preflight answer, and the plain-text 404 / auth 401 refusals).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:16:23 +02:00
Codeman maintainer 5a13073657 test(file-preview): pin the HTML page view, its fallback and the Page pill
The HTML branch of openFilePreview(), the Page pill and the fall-through
to the source view on a refused mint had no test. The jsdom harness from
file-preview-markdown.test.ts now drives them with a stubbed fetch:

- a successful mint renders an iframe at the minted URL whose sandbox
  has allow-scripts and never allow-same-origin, with the pill pressed;
- a refused mint shows the source view with its Edit button and hides
  the pill;
- the pill flips page and source and persists
  codeman:filePreviewHtmlRendered, and with the pref off the preview
  opens as source without a mint request.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:15:42 +02:00
Codeman maintainer 8f62faf130 refactor(file-preview): one rendersAsPage() for the terminal and the preview
What counts as an HTML page lived in three places: HTML_VIEW_EXTENSIONS
on the server, a /\.html?$/ regex in the terminal link provider and a
HTML_PAGE_EXTS set in panels-ui.js. The two frontend copies are now one
rendersAsPage() next to previewsInFileViewer() in constants.js, used by
the link provider (and through it every grid tile and the split's Pane
B, which share registerFilePathLinkProvider) and by openFilePreview()
and its toolbar. A new test pins it equal to the server's list.

toggleFilePreviewPage() now returns the preview load, so a caller (and
the coming frontend test) can await the flip.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:14:53 +02:00
Codeman maintainer 7b4832b3e4 feat(admin): count revoked html-view capabilities in the logout audit line
The admin forced logout already revoked the user's rendered-HTML
capabilities but recorded only the cookie sessions and web-tab
capabilities it retired. The `user.logout` audit entry now carries
`revokedHtmlViews` next to `revokedWebviews`, and the revocation test
reads it back from admin-audit.jsonl.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:14:00 +02:00
Codeman maintainer 7dd78d9d71 fix(sw): leave rendered HTML pages to the network
The fetch handler skipped only /api/, so a popped-out /html-view/ page (a
top-level navigation the worker intercepts) was put into CacheStorage
despite its `Cache-Control: no-store`, and the offline fallback could
serve it after logout had revoked the capability. /html-view/ is now
skipped like /api/. The new test runs the shipped sw.js against a stub
worker scope and drives its real fetch listener.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:13:29 +02:00
Codeman maintainer 06f1e5ddc8 fix(html-view): answer the rendered page's CORS preflight
A sandboxed page is opaque-origin, so a fetch() of its own data file with
a non-safelisted header preflights with `Origin: null`. The auth hook
refused the OPTIONS (the html-view exemption admitted GET and HEAD only)
and, without a password, the security-headers hook answered a bare 204
with no Access-Control-Allow-Origin, so the fetch failed either way.

The exemption now admits OPTIONS for a live capability, and
registerSecurityHeaders answers it itself with Allow-Origin `*`,
Allow-Methods `GET, HEAD` and Allow-Headers `*` (no credentials are
involved). An unknown or revoked capability still gets the 401, and
writes on the capability path are still refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:12:45 +02:00
Codeman maintainer 4544379b3b style(auth): put hasValidWebviewCapability's JSDoc back above it
The html-view helper had been inserted between the web-tab function's
JSDoc and the function, so that doc block described the wrong function.
The helper now sits after hasValidWebviewCapability with its own doc.
No behaviour change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:12:02 +02:00
Codeman maintainer fa23f8be2a feat(html-view): answer Range requests through the shared sendFileBody()
A rendered page's own <video> or <audio> could not seek, because the
route streamed a plain 200 without Accept-Ranges. sendFileBody() and
localFileSource() are now exported from file-routes.ts and the serving
route uses them, so it advertises Accept-Ranges and answers a Range with
206 + Content-Range (or 416), exactly like file-raw, the attachment raw
route and /api/download. The body sender hijacks the reply and copies
every header set before it (the sandbox CSP, CORS, no-store), which the
new test pins on a 206.

Media types stay in ASSET_TYPES: with Range support they work, and they
are served under the same sandbox CSP as everything else.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 17:11:50 +02:00