mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 23:19:43 +02:00
6d6e7da481db41a1918f64bca4590a4eff5ad462
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
6d6e7da481 |
fix(notifications): main Save keeps webhook edits, glue test, docs and nits (#523 review)
Merge-time fixes for the webhook notification channel (ntfy, Slack, Discord, generic JSON).
Minor 1, App Settings Save silently dropped webhook edits: the modal's main Save now
persists the webhook group beside the settings PUT, the same way it already saves the
model config (saveModelConfigFromSettings), but only when the group differs from what
loadWebhook() put on screen (_webhookPending), so an untouched group never re-PUTs. A
refusal (bad URL, enabled with no URL) shows a warning toast, keeps the modal open and
scrolls to the group with the pasted URL still in the box, instead of a success toast.
Send test now saves pending edits first, so it never tests the old URL while the box
shows a new one. The row says so in one line.
Minor 2, no test for the server.ts glue: new test/webhook-push-glue.test.ts drives the
private sendPushNotifications on a real (never started) WebServer with an EMPTY push
store and webhook.json in the instance data dir, delivering through the real
egress-guarded fetch to a local receiver: a permission prompt arrives with the
host-prefixed ntfy Title and body while Web Push is never called, an immediate repeat is
deduped, "response complete" is skipped under scope attention and sent under all, and a
disabled config or a non-push event sends nothing. Verified it fails when the webhook
call is moved below the "no subscriptions" return.
Minor 3, docs: webhook.json added to CLAUDE.md State Files; a Webhooks section in
docs/wiki/Notifications-And-Approvals.md (setup, what is sent, the secret URL, public
ntfy topics, local targets allowed, dedupe, instance-wide reach in multi-user mode) plus
a table row, and a line in Settings-Reference; new section 10c in
docs/security-architecture.md for the second outbound channel through the web-tab
egress guard.
Nits:
- Orphaned JSDoc: the webhook schema moved below the push schemas, so
PushSubscribeSchema has its comment back.
- Duplicated enums: WebhookUpdateSchema uses z.enum(WEBHOOK_KINDS/WEBHOOK_SCOPES), so
the schema cannot accept a kind the store would coerce away.
- describeError classifies egress refusals with isEgressBlockedError (the
CODEMAN_EGRESS_BLOCKED code anywhere in the cause chain) instead of a message regex;
tests pin a deep cause chain and that matching words alone are not a refusal.
- Markup: the URL input uses set-input, the whitespace-only line is gone, and the switch
row hints to pick a long random topic on public ntfy.sh.
- Remove a saved URL: a "Remove URL" button (shown only while a URL is saved, with a
confirm) sends { url: "", enabled: false }.
- Types placement: WEBHOOK_KINDS/SCOPES and WebhookKind/Scope/Urgency/Config/Result/Status
moved to src/types/push.ts (the IO-side WebhookMessage/Request/Fetch stay in the module).
Browser test extended: main Save persists a pending edit, a refused URL keeps the modal
open with the URL, Send test saves a newly pasted URL first, Remove URL clears it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
||
|
|
52267f8617 |
fix(terminal): drive the shipped Shift+Enter handlers and the Key tester cap in their browser tests (#520, #522 review)
- Minor: the Key tester "14 lines" test never pressed a key into the tester (the previous test blurred it, so the presses landed on <body> and the cap was never exercised). It now refocuses the field, asserts the focus, clears the log, checks 2 presses accumulate to 6 lines, then 4 presses of another key cap the log at exactly 14 with the oldest 4 lines evicted in order, and the readonly field stays empty. Verified to fail with the cap changed to 20. - Nit: the split-pane invariant implied Ctrl+Enter could use the CLI's declared newline chord. Reworded after checking the send-key route: Ctrl+Enter is always a real 0x0a, Shift+Enter is the declared capabilities.newline chord (0x0a unless the CLI declares another), sent on keydown only. The same imprecision in the auto-named sessions paragraph is corrected too. - Nit: docs/wiki/Settings-Reference.md now lists the Key tester row in the Terminal & Input table. - Nit: test/shift-enter-keypress.browser.test.ts exercised a hand-copied predicate named `shipped`. It now loads the real app from a real WebServer and presses real keys into the handlers terminal-ui.js (app.terminal, recording the real _sendInputAsync send path) and terminal-split.js (a real SplitTerminalPane) attach, recording the send-key POSTs through a fetch wrapper. It asserts no \r reaches either send path for Shift/Ctrl+Enter, exactly one send-key per press for the right session, and that Enter and Alt+Enter are untouched. The old keydown-only gate stays as a labelled reproduction of xterm's keypress behaviour on a bare Terminal. Verified to fail on both panes with the gate narrowed back to keydown. - Nit: the keypress trap is now written down beside the other key-gate rules (Command palette and shortcut registry): xterm runs the custom handler for keydown, keypress and keyup and drops only Ctrl/Alt/Meta keypresses, so a gate on a chord that can carry Shift alone must swallow every event type. The smart-copy keydown-only rule points at it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
01f403dc1b |
feat(claude): advisor tool support, per session and as an App Settings default
Claude Code's advisor tool (code.claude.com/docs/en/advisor) lets the session's main model consult a second, stronger model at decision points: before committing to an approach, on a recurring error, and before declaring a task done. Codeman can now start claude sessions with one. - `advisorModel` field on POST /api/sessions, /api/quick-start and /api/ralph-loop/start (fable, opus, sonnet or a full model id in those families; haiku cannot advise and is refused). Stored on the session and persisted, so respawn, boot restore and reboot restore keep it. Remote and docker quick-starts refuse it, as they refuse effort. - App Settings, Models, "Advisor" segment (Default / Sonnet / Opus / Fable), synced as `claudeAdvisorModel`. Run, resume and the Ralph wizard send it. Default sends nothing, leaving the CLI's own /advisor choice in charge. - Carried as the `advisorModel` key in the launch's single --settings JSON, merged with ultracode and the statusLine exporter, never the --advisor flag: `claude --advisor haiku` exits 1 at launch, which would leave a dead pane on every respawn, while the settings key degrades to no advisor. A launch without an advisor is byte-identical to before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> |
||
|
|
ac6236b268 |
fix(terminal): clean a copy once, and reach every pane that copies
Review fixes for #469. The Ctrl+C branch cleaned the selection to decide whether to copy and then passed that cleaned string to copyTerminalSelection(), which cleans again. The trailing trim is a fixed point, so that was safe until this PR; the margin strip is not, because it takes the lesser of the declared width and the run every line shares, so a second pass takes up to `margin` columns more. The branch now gates on the cleaned string and hands the raw one on. Verified in chromium with a real drag, a real Ctrl+C and a real clipboard read on a live claude pane: an on-screen ` fix(terminal): trim it` reaches the clipboard as ` fix(terminal): trim it`, and reverting the branch reproduces the reported ` fix(terminal): trim it`. Pane B of a split resolves its own width. `_cliGutterColumns()` and `_normalisedSelectionRange()` take the session and the terminal to read, defaulting to the primary pane's, so Pane B looks its own run mode up instead of keeping a margin Pane A drops on the same keystroke. Verified live with two claude panes open side by side. A detached session window (`/session/:id`) receives the gutter map. The injection sat inside the block that skips the run menu's payloads for a solo window, so the toggle worked in the main window and did nothing in the popup on the same device. It needs no availability probe, so it moved below that block and the solo window still carries none of the payloads it skipped before. The settings description said the width is measured and named Codex as exempt. Nothing is measured, and Codex is one of the two panes that are stripped. docs/wiki/Settings-Reference.md gains the row every Terminal and Input toggle carries. CLAUDE.md no longer says the clean touches trailing runs "and nothing else" one sentence before the leading-margin rule, and both it and docs/architecture-invariants.md record that the strip is not idempotent. Two round-trip tests run on a mode that declares a gutter, which the existing copyTerminalSelection cases could not, since they all use the harness default mode that declares none. The Ctrl+C branch itself is pinned at the source, because it lives inside initTerminal's attachCustomKeyEventHandler closure over a real xterm the vm harness cannot build. Both pins fail on the reintroduced bug. Gate: 7865 passed, 0 failed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3152ec801d |
docs(split-pane): short CLAUDE.md rule, stale module count, wiki entries, shortcut-handler caveat
CLAUDE.md previously only mentioned split-pane in the load-order list, with nothing in the Architecture/frontend prose the way every other feature gets, and its own module count was one stale (34, should have been bumped to 35 when terminal-split.js was added). Add a short pointer-style paragraph next to the other terminal features, fix the count. docs/wiki/The-Dashboard.md's header button table and docs/wiki/Settings-Reference.md's header chips list are the two user-facing surfaces that never mention Split at all; added both, plus a note that the feature is desktop-only regardless of the setting. docs/split-pane-sessions-plan.md: recorded the one design note that isn't a code change — the global capture-phase shortcut handler always resolves against Pane A, so Ctrl+L/Ctrl+W typed into Pane B affects the other session. Not fixed for v1, same reasoning as the rest of the "deliberately plainer" section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
98d26e14d9 |
docs(wiki): document Custom Model Endpoints and the Run-menu picker
New docs/wiki/Custom-Model-Endpoints.md (auto-synced to the live GitHub wiki on push to master, per docs/wiki/Contributing.md) covers turning the feature on, adding an endpoint, the Run-menu picker's one-off-run behaviour, the per-harness confidence table, and what it deliberately does not do yet (remote/Docker sessions, live hot-swap). Linked from the sidebar, from Agent-CLIs.md's "Read next" list plus a short pointer section, and from Settings-Reference.md's Models section. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RqZeHrRS6DYcGcGX2p9EwG |
||
|
|
bd286bf502 |
docs(wiki): catch the manual up to 1.29.0 and add the three run modes it never had
The wiki was written for seven run modes and never received Grok Build, DeepSeek Harness or OMP. They now appear everywhere the others do: the modes table and per-CLI notes, install commands, environment prefixes, the Quick Start table, the requirements rows, the vocabulary, and every "seven modes" count. The 1.27 to 1.29.0 changes land on the pages that own them: attaching a case to an existing container, multi-case adoption and the copy-a-case picker (Docker Cases); file reads over ssh in remote cases and what stays unavailable (Remote SSH Sessions, Working With Files, Security); single-page app routing, frame recovery, localhost links as tabs and the egress guard (Web Tabs); DeepSeek as the one non-Claude mode with real stop/blocked signals and Approvals items, Codex's own work detection, last-response, the model-endpoint routes and refreshed counts (HTTP API, Driving From An Agent, Hooks, Notifications, Keeping Agents Running, Core Concepts); Shift+drag, right-click copy, Auto Copy, the Ctrl+Z guard, font weight, the vertical rail and its activity sort (Keyboard Shortcuts, Input And Voice, The Dashboard, Settings Reference); the 600px phone cutoff, Codex shift arrows and iPhone Duo (Mobile Guide); the Docker Compose route and its update rule (Installation, Running As A Service); four new symptom entries and a "which CLIs" question (Troubleshooting, FAQ). Custom model endpoints are deliberately left to #430, which adds that page and edits Agent CLIs, Settings Reference and the sidebar; these edits stay out of the regions #430, #428 and #376 touch, and all three still merge cleanly on top. Both READMEs: the web-tab menu entry is labelled "Add URL" in the UI, not "Add dashboard". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
5b920cb43d |
feat(sessions): land auto-naming opt-in, in the prefix form, from the first user prompt only
Finishes #376. The contributed keystroke tracker sat on the raw byte stream and named tabs wrong five ways (every prompt, every write path, a bare Esc eating the next prompt's first character, pasted newlines as Enter, any CSI clearing the draft) and replaced the whole name, which dropped the case from the tab and reset the w<n> counter. This lands the feature with each of those closed: - First prompt means the first: applyAutoName() flips a placeholder to `auto` whether or not the string changed. nameSource is now the tri-state placeholder | auto | manual; the name setter is the only manual path. - Only user-originated input counts: write()/writeViaMux() take SessionWriteOptions.fromUser, set by the browser WS path and POST /input only, so Ralph, respawn, cron, approvals and the trust-dialog keys can never name a tab. A startMode 'shell' CLI never feeds the tracker (a capability, not an id check); the send-key route feeds trackUserInput() because its line feed bypasses the session. - Prefix form `w3-case: title`: parseSessionPrefix() already renders it as the title with the prefix in the tooltip and the next-session counter still matches it. Composed within MAX_SESSION_NAME_LENGTH. - Tracker rules per key: bare Esc resolves at chunk end; mouse/focus reports, Tab, cursor keys, Shift+Tab are no-ops; Up/Down and Ctrl+P/N/R taint the draft so Enter submits nothing rather than a fragment; bracketed-paste newlines and Ctrl+J / Shift+Enter join with one space; the draft keeps its head past 8192 code points; an escape past 64 bytes is abandoned. - Title: slash commands by shape (a path is a prompt), `!` escapes refused, first sentence only past 8 code points ("e.g." is not a title), 72 code points on a word boundary. - Synced `autoNameSessions` setting, default OFF (the prompt reaches mux-sessions.json, session:updated and /api/search), App Settings -> Appearance -> Tabs, read fresh per prompt after the eligibility check. Tests: test/session-auto-name.test.ts (tracker, title, composition, ownership, emit gating), the wiring test (once, prefix, setting off, manual protected), test/routes/session-name-routes.test.ts (PUT /name flips to manual and persists). Verified live on an isolated instance: API and browser-typed prompts name the tab, a second prompt does not, shells and renamed tabs are untouched, nameSource survives a restart. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
7936a75e28 |
feat(terminal): Auto Copy, put a finished selection on the clipboard
App Settings > Terminal & Input > Selection & clipboard > Auto Copy
Selection (`autoCopySelection`, per-device, default OFF). With it on,
highlighting text in the terminal copies it: mouse drag, double-click
word, triple-click line, and the phone long-press selection. Ctrl+C is
untouched and still copies on demand.
Three things decide the shape of it:
* It fires at the END of a gesture, never in onSelectionChange. That
callback runs for every cell a drag crosses, so copying there would be
one clipboard write per mouse move. It only arms a pending flag; a
document-level mouseup listener flushes, and the touch path calls the
flush itself because it preventDefaults its touchend and no mouseup
ever arrives there.
* The flush is synchronous inside the handler, because both clipboard
paths need user activation: Firefox gates navigator.clipboard
.writeText on it, and execCommand('copy'), the fallback the plain-HTTP
LAN install lands on, has to run in the gesture's own task. A timer or
a wait for onSelectionChange loses it, invisibly in Chrome.
* It deliberately does NOT do what copyTerminalSelection() does. That
one clears the selection (so a second Ctrl+C is an interrupt) and
focuses the terminal. Clearing would make text vanish under the cursor
that just highlighted it, and focusing opens the on-screen keyboard
over it on a phone. Focus is instead restored to whatever held it,
which only matters for the execCommand fallback.
Guards are pure in decideAutoCopy() (constants.js): off, blank or
whitespace-only text, and a 1M-char cap, since a drag off the top of the
viewport autoscrolls and one gesture can sweep the whole 50k-line
scrollback. Past the cap the copy is refused rather than truncated, with
a toast pointing at Ctrl+C.
Feedback is silent on success except once per page load, so a feature
that works by doing nothing visible can still be told from a dead
toggle; failures and refusals toast, throttled to 10s.
Per-device on both counts the settings rule requires: in `displayKeys`
and absent from the .strict() SettingsUpdateSchema, because clipboard
access differs by device and by origin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
f3cb7696f0 |
docs: publish docs/wiki as the user manual, with a sync workflow
30 pages covering install, concepts, the dashboard, the agent CLIs, unattended
runs, remote and Docker cases, security and the HTTP API, plus a sidebar and a
footer. The wiki repo has no CI and no review, so docs/wiki is the source of
truth and .github/workflows/wiki-sync.yml mirrors it on every push to master.
The workflow refuses to mirror when docs/wiki is missing or holds no pages,
because it deletes before it copies and would otherwise publish the deletion of
every page. The footer carries a {{VERSION}} placeholder stamped at publish
time rather than a hand-written version, which went stale on every release.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|