Commit Graph
33 Commits
Author SHA1 Message Date
Codeman maintainer b34fcaf928 feat(web-tabs): open dashboard URLs as tabs beside agent sessions
Adds a "Web / URL" section to the Run dropdown. A saved URL renders as a tab in
the same strip as Claude/Codex/Gemini sessions, with the same Alt+1..9 numbering,
so Codeman is one mission control instead of Codeman plus a pile of browser tabs.

A webview is NOT a sixth SessionMode: no PTY, no tmux, no respawn, no idle
detection. It is a separate resource sharing only the tab strip and the main
content area, the same call that keeps Docker and remote-SSH as case overlays.

Dashboards are proxied through Codeman's own origin, because a direct iframe
fails three ways at once in the shipped deployment: prod serves HTTPS behind
tailscale serve, so http:// targets are hard-blocked as mixed content (with no
override at all on iOS Safari); Grafana/Portainer-class dashboards send
X-Frame-Options: DENY; and our own default-src 'self' CSP blocks cross-origin
frames. Proxying dissolves all three and leaves the production CSP byte-for-byte
unchanged, since /webview/... is already covered by 'self'. A useful side effect:
the fetch happens server-side, so a tailnet-only dashboard is reachable from a
phone that is not on the tailnet.

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 correspondingly exempt from the cookie and Origin checks, because
a sandboxed iframe is opaque-origin: it sends no SameSite=lax cookie and its
writes arrive with Origin: null. The Host allowlist is never bypassed. A second
Referer-keyed form of the exemption exists for root-absolute assets and is fenced
to safe methods on non-/api, non-/ws, non-/q paths.

Iframes omit allow-same-origin unless a URL is explicitly marked trusted, since a
proxied page is served from Codeman's own origin and could otherwise read this
document and drive the agent-spawning API. Authorization and codeman_session are
stripped upstream in BOTH modes, so CODEMAN_PASSWORD cannot leak into a dashboard.

Two things only a real browser reveals, both presenting as the dashboard's own
"Failed to fetch" while the page itself renders fine:

- Runtime-built root-absolute URLs (fetch('/api/data')) escape <base href> and
  land on Codeman's root. Widening the Referer fallback into /api would trade
  security for it, so an injected shim patches fetch/XHR/WebSocket/EventSource
  inside the frame instead, removing the class rather than the guard.
- An opaque-origin document CORS-checks every request, including to the host it
  was served from. Script/css/img loads are not CORS-checked, which is why the
  page renders while its API calls die. The proxy now emits CORS headers and
  answers preflights itself. registerSecurityHeaders answered every OPTIONS with
  a bare 204 before routing, carrying no ACAO for Origin: null, so that
  short-circuit now exempts a valid capability.

Neither is reproducible with curl, which does not enforce CORS.

Also fixes a pre-existing bug found on the way: .toolbar has backdrop-filter,
making it a stacking context that trapped .run-mode-menu's z-index:1000, so
.welcome-overlay painted over the whole Run menu. With no session open, every
item in it (Claude Code included) was unclickable.

Verified end to end against a real tailnet dashboard: live data, WebSocket push,
no failed requests, and switching tabs does not reload the frame. 98 new tests
cover the pure rewrite helpers, the CORS helper, the shim's rewrite logic, route
CRUD, and every edge of the auth exemption.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 17:06:36 +02:00
Codeman maintainer f496e35d71 feat(multiuser): phase 1, user store, mode plumbing, CLI
Opt-in multi-user foundation (off by default; no behavior change without
CODEMAN_MULTIUSER/--multiuser):

- src/config/multiuser.ts: isMultiUserMode(), getUserSpacesDir()/userCasesDir(),
  maxUsers(), maxSessionsPerUser() (per-user fairness cap = global/2).
- src/types/user.ts: UserRecord/PasswordHash/AuthUser/PublicUser/UserRole.
- src/user-store.ts: ~/.codeman/users.json (atomic tmp+rename, mode 0600, short
  TTL cache). scrypt hashing with per-record params + timingSafeEqual verify plus
  rehash detection; createUser/setPassword/updateUser/deleteUser with last-admin
  invariants; guarded deleteUserSpace (symlink + realpath confinement, section 8);
  pure section-6.3 resolvers (resolveClaudeModeForUser downgrades bypass to auto
  for non-granted users; canRunPrivilegedCommands); bootstrapInitialAdmin.
- src/cli.ts: "codeman users add|passwd|list|rm" (hidden prompt or
  --password-stdin) operating directly on users.json; a --multiuser flag on the
  web command.

Tests: test/user-store.test.ts (29 tests: hashing/verify/rehash, username
validation, atomic 0600 write, last-admin invariants, 6.3 resolvers,
delete-space guards, bootstrap).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:58:51 +02:00
Codeman maintainer 83779cab4d Merge PR #141 from chatgptkrylor/feat/scheduler: recurring cron-style scheduled jobs
Includes review fixes: multi-line prompt rejection, prompt-file confinement hardening (realpath + attachment-guard blocklist), per-job autoClosePreviousSession lifecycle, live-session-only concurrency counting, wired launchCommand.
2026-07-12 20:01:16 +02:00
Codeman maintainer 84ab4ff07b fix(review): harden cron security, session lifecycle, skip policy (PR #141)
- Reject multi-line prompts end-to-end: schema refines on promptText/
  launchCommand, runtime check in resolvePrompt (prompt-file content;
  trailing newlines tolerated), matching cron-ui form validation — delivery
  is single-line only, so multi-line was silently corrupted (typed mode
  fused lines, paste mode submitted partials)
- Close the workingDir confinement bypass (arbitrary server-side file read,
  e.g. workingDir=/proc + /proc/self/environ): realpath-resolve workingDir
  before the containment check, reject '/' and blocked/pseudo-fs trees
  (/proc, /sys, /dev + the attachment-guard blocklist) at fire time AND at
  job create/update (workingDir must exist and be a directory)
- Session lifecycle: new per-job autoClosePreviousSession (default true,
  recurring schedules only; ignored for 'once') — the previous run's
  still-open session is closed via the normal cleanupSession path when the
  next run fires; UI switch added; 50-session cap math documented in
  docs/cron-guide.md §8
- skip_if_same_agent_running: count only live sessions (exclude
  stopped/error dead tabs), exclude sessions created by this job's own runs
  (fixes the fire-once-then-skip-forever self-deadlock), and a skipped
  'once' job stays armed and retries next tick instead of being consumed;
  liveness filter mirrored in cron-ui _countActiveAgents
- Wire launchCommand (was accepted+documented but dead): shell mode sends
  it via writeViaMux as the first input line after startShell readiness
  (single-line, schema-enforced); form field shown for shell agent type
- Record delivery failures: a false writeViaMux result now fails the run
  instead of recording a false 'prompt_sent'
- Cap saved jobs at MAX_CRON_JOBS (100) to bound state.json growth
- Surface field-specific schema messages (drop parseBody custom
  errorMessage on cron create/update)
- Tests: workingDir create/update validation, /proc bypass regression,
  single-line enforcement (schema+runtime+trailing-newline tolerance),
  live/own-session skip filtering, once-skip re-arm, auto-close on/off/once,
  job-count cap

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 19:25:42 +02:00
Codeman maintainer 246f7b532d fix(review): clamp env-path trim below max; revert unwired scrollback raise (PR #138)
- UNBOUNDED-MEMORY: DEFAULT_TERMINAL_BUFFER_TRIM_BYTES from CODEMAN_TRIM_TERMINAL_TO
  had no relation to DEFAULT_TERMINAL_BUFFER_MAX_BYTES — setting only
  CODEMAN_MAX_TERMINAL_BUFFER=2097152 left the 24MB trim default in force, making
  BufferAccumulator.trim() (slice(-trimSize)) a no-op: unbounded growth past the cap
  plus a full string re-join on every append (O(n²)). Trim default is now clamped to
  75% of the resolved max (the 24MB/32MB default ratio, preserved as hysteresis);
  regression test re-evaluates the module under the env via vi.resetModules.
- OVERCLAIM: reverted DEFAULT_TERMINAL_SCROLLBACK_LINES 100k -> 50k — it has zero
  consumers; browser xterm scrollback is the separate hardcoded DEFAULT_SCROLLBACK
  (50k) in constants.js and deliberately stays 50k (mobile-memory hazard). The tmux
  history-limit raise (50k -> 100k) and PTY 32MB/24MB raise remain (those are wired).
  Module docstring now claims only what is wired; fixed the stale tmux-manager.ts
  comment saying the tmux limit "matches the xterm-side default in constants.js".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 12:39:46 +02:00
Aamer Akhter ad71a92f29 COD-80 raise terminal history/scrollback/buffer defaults
Bump the centralized terminal-history defaults: tmux scrollback 50k->100k and
PTY buffer cap 2MB->32MB (trim 1.5MB->24MB). Both remain env/settings overridable
and bounds-clamped. Worst-case 20-session buffer budget rises 40MB->640MB.
Stacked on the terminal-history config commit.
2026-07-01 13:00:08 -04:00
Aamer Akhter 8c0c94540c COD-80 centralize terminal history/scrollback/buffer limits into config
Introduce src/config/terminal-history.ts: one place for terminal scrollback,
tmux history-limit, and PTY buffer byte caps, each overridable via env var or
the settings object and bounds-clamped via resolveTerminalHistoryConfig().
Defaults match the prior hardcoded values, so this is behavior-neutral. Wires
the resolver through buffer-limits, tmux-manager (incl. a setHistoryLimit so a
settings change applies live), session, server, system-routes, session-routes,
schemas, and the config port. Adds 4 optional settings keys (terminalScrollback
Lines, tmuxHistoryLimit, terminalBufferMaxBytes, terminalBufferTrimBytes) with
bounds + a trim<=max cross-check.
2026-06-30 19:52:49 -04:00
KrisandClaude Opus 4.8 d9c2c6420d fix(cron): harden cron fixes against adversarial-review findings
Two blind adversarial reviewers found real holes in the prior cron commits:

SECURITY (was CRITICAL): the prompt-file guard was blocklist-only by default,
so promptFilePath:/proc/self/environ leaked the SERVER PROCESS's entire
environment (every secret) into the agent session, and /dev/zero or a FIFO
caused an unbounded readFile → OOM/hang DoS. A denylist is the wrong posture
for an exfil-into-LLM sink. resolveSafePromptPath now:
  - confines the realpath-resolved file to the job's working dir (ALLOWLIST) —
    closes /proc, /dev, other homes, modern cloud-cred paths, and symlink escapes
  - requires a regular file (rejects dirs/FIFOs/char devices)
  - caps the read at MAX_PROMPT_FILE_BYTES (1 MiB)
  - keeps the /etc,/root,secrets blocklist as defense-in-depth

LOGIC:
  - once-rearm (was MED, defeated in prod): the edit UI round-trips the full
    job, so the field-PRESENCE re-arm check always fired → a renamed fired
    once-job could be resurrected via edit→re-enable. Now compares schedule
    VALUES; an unchanged schedule never re-arms.
  - skipped-run history (was HIGH): recording a skip every tick was unbounded
    state.json growth. Now coalesces consecutive skips (one record per streak)
    and prunes global run history to MAX_CRON_RUN_HISTORY (500), covering the
    launch path too.
  - skip bookkeeping (was MED): a skip no longer advances lastRunAt (nothing
    ran); lastStatus still reflects 'skipped'.

Regression tests added/updated (43 pass): /proc/self/environ + outside-workspace
+ symlink-escape + non-regular + oversized all blocked, in-workspace file
passes; UI-path once resurrection blocked; consecutive skips coalesce to one
record; skip leaves lastRunAt null.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PmvZR12aX2v8K7YhqxPUAU
2026-06-29 12:05:04 +05:30
KrisandClaude Opus 4.8 9feaa0d6e5 refactor(cron): rename scheduler feature to cron
Rename the recurring-jobs feature scheduler->cron to disambiguate from the
legacy ScheduledRun system (/api/scheduled), which is left untouched:

- ScheduledJob->CronJob, SchedulerService->CronService
- /api/scheduler/jobs -> /api/cron/jobs; SSE scheduler:* -> cron:*
- state keys cronJobs/cronJobRuns
- files moved to src/cron/, cron-routes.ts, cron-port.ts, types/cron.ts
- frontend cron-ui.js, #cronModal, menu "Cron"
- docs moved to docs/cron-discovery.md + docs/cron-build-brief.md, README guides
- new tests: cron-service.test.ts, cron-time.test.ts

Green: tsc, lint, frontend-syntax, format, 30 cron + 9 legacy scheduled-runs tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PmvZR12aX2v8K7YhqxPUAU
2026-06-29 11:35:56 +05:30
KrisandClaude Opus 4.8 6ae86b53f6 feat(scheduler): add cron-style scheduled jobs (backend)
Adds a saved/named scheduling layer on top of Codeman's existing session
primitives. Distinct from the legacy run-now ScheduledRun concept.

- types/scheduler.ts: ScheduledJob + ScheduledJobRun
- state-store: persist scheduledJobs/scheduledJobRuns in ~/.codeman/state.json
- scheduler/scheduler-time.ts: pure once/interval/daily/weekly next-run math
- scheduler/scheduler-service.ts: CRUD, Run Now, due-checker tick, run history;
  reuses SessionPort (create -> start -> writeViaMux) for launches
- web/routes/scheduler-routes.ts: /api/scheduler/jobs CRUD + run + history
- web/schemas.ts: zod validation with schedule-type-aware refinements
- web/sse-events.ts: scheduler:* events
- server.ts: wire service into route context + 30s background tick loop
- test/scheduler-time.test.ts: 14 unit tests for next-run calculations

Phase 1 discovery recorded in SCHEDULER_DISCOVERY.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rp7JhmQXcYJhmxFMdZuuah
2026-06-27 08:10:40 +05:30
Codeman maintainer 368fc20fc2 fix: address PR review findings for Gemini run mode + Ralph todo-config
Gemini (PR #134) blockers:
- runGemini() now unwraps the {success,data} envelope: status check reads
  .data.available, quick-start reads data.data.sessionId (was reading the raw
  shape, so the Run-Gemini button could never start a session).
- setGeminiEnvVars() now uses the socket-scoped ${this.tmux()} setenv instead of
  bare tmux — Gemini/Google auth env vars were targeting the wrong tmux server
  and silently failing on every install.

Gemini parity polish:
- gemini tab-mode badge ('gm') + .tab-mode.gemini CSS; kill-dialog label
  'Kill Tmux & Gemini'; codeman doctor dependency-registry entry; export
  isGeminiAvailable from utils barrel; COLORTERM=truecolor + unset NO_COLOR;
  add gemini to isAltScreenStripMode (Ink TUI, repaints inline like Codex/Claude).
- Revert 4 system-routes.test.ts envelope assertions weakened to
  (body.message ?? body.error) back to (body.success === false).
- Add a runGemini() vm-sandbox test that drives the envelope path end-to-end.

Ralph todo-config (PR #135): maxTodos/todoExpirationMinutes are now persisted
and read back — surfaced via the loopState getter (RalphTrackerState) into
toState()/SSE broadcast and restored in restoreState(), mirroring maxIterations.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 00:26:06 +02:00
Codeman maintainer 50b8f1d9a0 feat(mobile): large + multi-image uploads from the camera-roll picker
The mobile copy/paste overlay's "🖼 Image" button (and drag-drop / paste)
now handles real-world photo batches:

- Up to 20 images per batch, uploaded with bounded concurrency (3) and a
  live "Uploading N/M…" progress toast; a final summary reports successes,
  any failures, and whether the 20-cap trimmed the selection (no silent
  truncation).
- Per-file upload limit raised 10MB → 50MB (MAX_PASTE_IMAGE_BYTES in
  buffer-limits.ts, env-overridable) so full-resolution phone photos and
  large screenshots aren't rejected.
- Very large images are downscaled to <=4096px longest edge before upload:
  fixes iOS Safari's ~16.7M-px <canvas> limit (which made huge photos fail
  to re-encode and fall back to an original that tripped the magic-byte
  check), and keeps batch uploads fast and small.
- Fix a latent concurrency bug the batch path exposed: the first parallel
  uploads to a session raced on `mkdir(.claude-images)` and the EEXIST
  losers 500'd. mkdir now treats an existing real directory as success
  (re-verifying it isn't a planted symlink), so concurrent uploads succeed.

Verified end-to-end in a real browser (Playwright): downscale, >10MB
server acceptance, 20-cap, 20/20 concurrent uploads landing on disk.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 23:12:41 +02:00
Codeman maintainer c15c19fab7 feat(ultracode): master-detail tab for Workflow/ultracode run visualization
Opt-in (showUltracodeAgents, default OFF) panel that visualizes ultracode /
Workflow-tool runs like Claude Code's "working agents" TUI: LEFT = runs + phases
(selectable tasks), RIGHT = each run's agents with model, live state, tokens
burned, and tool calls.

Standalone — ZERO edits to subagent-watcher.ts. A new workflow-run-watcher.ts
singleton globs the run-state tree (~/.claude/projects/*/*/workflows/wf_*.json,
disjoint from the transcript tree), strips the heavy script/scriptPath/result/logs
fields (174KB -> ~25KB/run), and emits workflow:run_* SSE events. The LEFT list
ships lightweight summaries (getLightState replay + SSE); the RIGHT pane fetches
the full run (with agents[]) via GET /api/workflows/:runId on selection.

Backend: workflow-run-watcher.ts, types/workflow-run.ts, config/workflow-config.ts,
3 SSE events, getLightState workflowRuns replay, GET /api/workflows[/:runId],
showUltracodeAgents schema key + boot-gate (default OFF) + live toggleService.
Frontend: ultracode-panel.js (debounced master-detail render, run/phase select),
header launcher (btn-ultracode-agents--hidden marker -> mobile-guard-exempt),
App Settings toggle (SYNCED, deliberately not in displayKeys).

Agent states on disk are start|progress|done (start=queued; done has
durationMs/resultPreview). Tests: workflow-run-watcher (9), workflow-routes (3).
Verified: tsc/lint/prettier/frontend-syntax/public-assets/mobile-header-guard
clean; full test:ci green (2986 passed); live server + Playwright e2e against 25
real runs (28-agent grid, phase filter, OFF hides launcher).

Design: docs/ultracode-agent-viz-plan.md (rev. 3).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 08:40:05 +02:00
Claude (Codeman maintainer) c101cc8716 fix(doctor): correct Node minimum, drop phantom gemini, add pdftoppm, validate --category
Review fixes on top of the `codeman doctor` checker:

- Node minVersion 18.0.0 -> 22.0.0. package.json engines is ">=22.0.0" and the docs/CI
  require Node 22+, so doctor was green-lighting Node 18-21 (a false pass).
- Remove the phantom `gemini` registry entry. Codeman has no Gemini backend
  (SessionMode = 'claude' | 'shell' | 'opencode' | 'codex'); the entry advertised a
  dependency that nothing uses.
- Add `pdftoppm` (poppler) to the office group. document-thumbnailer.ts calls pdftoppm
  with no fallback as the sole PDF/Office first-page thumbnail renderer, yet it was
  absent from the registry, so doctor never reported it missing.
- Fix the `--category` mismatch: the help advertised `documents|media` categories that
  the ToolCategory type/registry never defined, and an unknown category silently
  produced an empty "all healthy" table. Introduce TOOL_CATEGORIES as the single source
  of truth (type + help + validation); an invalid `--category` now errors with the
  valid list and exits 2.

Verified: tsc, lint, format:check all clean; both dependency tests pass (20);
`doctor` runs correctly (Node 22.22 ok, pdftoppm detected, no gemini), `--category media`
errors with exit 2, `--category office` lists libreoffice/pdftoppm/msoffice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:24:46 +02:00
Aamer Akhter 585127deb2 Add codeman doctor tool-dependency checker (COD-45)
Environment-aware dependency probe (linux|darwin|win32|wsl) with a static
registry, an injectable ProbeHost seam for testing, grouped table + `--json`
output, and a non-zero exit when a required dependency is missing/outdated.
Node and tmux are the only hard-required tools; the agent CLIs and document
converters (LibreOffice / MS Office via WSL interop) are optional. CI-safe
unit tests (no tmux, injected host).
2026-06-14 12:25:49 -04:00
Aamer Akhter f1c64994ad COD-37 add server-side attachment pipeline (registry, magic-link, path guard)
Adds the foundation for serving local files to the browser as live external
attachments with a stable id, so requests never carry arbitrary absolute paths.

- attachment-registry: in-memory, session-scoped registry. registerExternalAttachment
  validates an absolute path, resolves symlinks, enforces the path guard, and mints
  an `att_<uuid>` id; records are cleared when the session is removed.
- attachment path guard: a configurable blocklist (secret locations + /root,/etc
  trees, extendable via attachmentBlockedPaths / CODEMAN_ATTACHMENT_BLOCKED_PATHS)
  plus an optional, default-off workspace-confinement mode. Shares one
  sensitive-path blocklist (web/sensitive-path.ts) with /api/download, which is
  refactored to use the extracted module instead of an inline copy.
- terminal magic links: the session scans output for codeman://attach?path=... and
  emits `attachmentRequested`; the web server registers the file and broadcasts an
  `attachment:detected` SSE event. `codeman attach <path>` (CLI) prints the magic
  link or POSTs directly when a session id is known.
- image watcher: detects png/pdf/docx/pptx dropped into a session's working dir and
  emits `attachment:detected`.
- routes: POST /api/sessions/:id/attachments (register) and
  GET /api/sessions/:id/attachments/:attachmentId/raw (serve), both re-checking the
  guard before streaming.

Document previews/thumbnails and the attachment-history drawer build on this
foundation and land separately.

Verified: tsc --noEmit, lint, format, frontend-syntax, full test:ci (2846 passed),
and a server boot smoke (/api/status 200).
2026-06-11 10:27:09 +02:00
Aamer Akhter 42f0b28c75 feat(security): hook-event auth secret + tunnel password guard
Two hardening fixes for the public-tunnel exposure path (COD-54 / COD-55).

COD-54 — gate the /api/hook-event localhost bypass when a tunnel is up:
`cloudflared --url http://127.0.0.1:port` proxies internet traffic INTO the
loopback origin, so a tunneled hook request arrives with req.ip === 127.0.0.1
and the old bare-localhost bypass would pass it unauthenticated. Now:
- tunnel running  → bypass requires a shared per-instance hook secret
  (X-Codeman-Hook-Secret header; constant-time compare) + per-IP rate limiting
- tunnel not running (loopback-only, the normal case) → unchanged, so
  already-deployed credential-less hooks keep working.
New src/config/hook-secret.ts; auth middleware takes a getTunnelRunning probe
(wired from server.ts via tunnelManager.isRunning()).

COD-55 — refuse starting the Cloudflare tunnel without auth:
enabling the tunnel publishes full terminal control to a public URL; with no
CODEMAN_PASSWORD the auth middleware is inactive and the bind guard never trips
(tunnel binds loopback). PUT /api/settings now refuses tunnelEnabled:true with a
403 (before persisting) unless CODEMAN_PASSWORD is set or
CODEMAN_ALLOW_UNAUTHENTICATED_NETWORK=1 is acknowledged. New
isUnauthenticatedNetworkAcknowledged() in network-auth-policy; settings-ui
surfaces the refusal as an error toast and reverts the toggle.

Scope: the always-on CSRF/Origin guard, Host-header allowlist, and
network-auth-policy itself are already upstream (#113) and not re-proposed here.

Verification: tsc, eslint, prettier, check:frontend-syntax clean; full test:ci
green (2723 passed), incl. test/cod54-hook-event-auth and
test/routes/system-routes-tunnel-guard.
2026-06-10 12:25:26 -04:00
arkonandClaude Opus 4.8 cf6fabc070 fix(web): address self-review findings on #103 (master-safe defaults + hardening)
Make the branch genuinely master-mergeable and fix several review findings:

- Defaults are now prod-safe: CODEMAN_INSTANCE defaults to '' (→ ~/.codeman,
  -L codeman) and the web port back to 3000, so an existing install upgrades
  cleanly. Port also honors a new CODEMAN_PORT env var. Run the beta isolated
  alongside prod with scripts/run-beta.sh (CODEMAN_INSTANCE=beta + PORT 5000).
- .gitignore: anchor the root `public` symlink rule to `/public` (a bare
  `public` also swallowed src/web/public, silently un-staging new web assets);
  ignore the gesture wasm/model binaries explicitly instead.
- span-displays: add a macOS-only guard (400 elsewhere instead of spawning a
  bash that fails invisibly); extract resolveSpanUrl() for unit testing.
- server.ts: memoize asset-version stat() calls (~1s TTL) so each index render
  doesn't re-stat every script/link tag.
- styles.css: hide the multi-monitor button in solo (detached) windows.
- app.js: require two consecutive unanswered roll-calls before redocking, so a
  timer-throttled background popup isn't wrongly un-marked.
- index.html: make the "skip to terminal" link base-href-safe (onclick scroll)
  so it doesn't navigate to the dashboard from a /session/:id window.
- Tests: test/config/instance.test.ts, test/routes/system-span-displays.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:41:46 +02:00
arkonandClaude Opus 4.8 b00a680d42 feat(web): session detach/undock + beta instance isolation (port 5000)
Detach a session tab into its own browser window and back.

Detach/undock:
- GET /session/:id serves the SPA in "solo mode", reusing the existing
  client (terminal, local-echo overlay, reconnect) so no terminal code is
  duplicated. One PTY already fans out to N SSE/WS clients, so a detached
  window is just another live client — no server fan-out work was needed.
- A pop-out icon per tab; detached tabs show a badge and focus the popup on
  click; closing the popup re-docks. Cross-window state via BroadcastChannel
  plus a WindowProxy poll, and survives a dashboard reload (roll-call).
  app.detachSession(id) is a single idempotent entry point (future gesture
  hook). <base href="/"> so relative assets resolve under /session/:id.

Beta-branch isolation (so it can run alongside a prod Codeman):
- Default port 3000 -> 5000.
- New src/config/instance.ts derives the data dir and tmux socket from
  CODEMAN_INSTANCE (default "beta"): ~/.codeman-beta + tmux -L codeman-beta.
  Every ~/.codeman path now goes through dataPath()/getDataDir() (state,
  mux-sessions, settings, push keys, lifecycle log, screenshots, certs,
  linked-cases, subagent window state). Overridable via CODEMAN_INSTANCE /
  CODEMAN_DATA_DIR / CODEMAN_TMUX_SOCKET. Prevents a second instance from
  discovering and attaching PTYs to the first instance's live tmux sessions.

Verified: tsc / eslint / prettier / lockfile clean; Playwright E2E (27 checks)
for detach/solo/redock; default isolation confirmed to see zero real sessions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 03:53:54 +02:00
arkonandClaude Opus 4.6 2709b2fe49 feat: make buffer size limits configurable via environment variables
Allow overriding MAX_TERMINAL_BUFFER_SIZE, TRIM_TERMINAL_TO, MAX_TEXT_OUTPUT_SIZE,
TRIM_TEXT_TO, and MAX_MESSAGES via CODEMAN_* env vars, falling back to existing
defaults. Enables users with fewer sessions or more RAM to tune buffer sizes
without patching source.

Closes #48

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:40:40 +01:00
arkonandClaude Opus 4.6 98fa55b7b2 chore: codebase cleanup — remove dead code, consolidate imports, extract constants
- Remove 3 unused exported constants (TRIM_MESSAGES_TO, MAX_TERMINAL_COLS, MAX_TERMINAL_ROWS)
- Consolidate 8 direct util imports into barrel imports (./utils/index.js)
- Extract magic number 8191 to FILE_PEEK_BYTES constant in buffer-limits.ts
- Add explanatory comments to 9 undocumented .catch(() => {}) handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:50:40 +01:00
arkonandClaude Opus 4.6 0717cfbfec refactor: codebase cleanup — dead code, regex helper, centralized constants, tests
- Remove unused validateTokenCounts/validateTokensAndCost exports and PlanPhase type alias
- Add execPattern() helper to eliminate 8 repetitive .lastIndex=0 + exec() loops
- Centralize 11 magic number constants into config/ai-defaults.ts and config/server-timing.ts
- Remove stale src/tui from tsconfig.json exclude
- Fix CLAUDE.md inaccuracies (session helpers, app.js line count, module count)
- Add 316 new tests: LRUMap (38), StaleExpirationMap (42), BufferAccumulator (33),
  respawn helpers (142), system-routes expansion (11→61)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:33:44 +01:00
arkonandClaude Opus 4.6 2b61a8db1b fix: add SSE padding to flush Cloudflare tunnel buffers for real-time events
Cloudflare quick tunnels buffer small SSE responses, causing tab creation
and other UI events to arrive late on mobile. Adds ~8KB SSE comment padding
(ignored by EventSource) to force the proxy to flush immediately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 18:24:14 +01:00
arkonandClaude Opus 4.6 18217268bf fix: extract QR auth magic numbers into named constants, add 16 security tests
Replace hardcoded per-IP rate limit (10) and cookie maxAge (86400) in
system-routes.ts with QR_AUTH_FAILURE_MAX and AUTH_SESSION_TTL_MS/1000
so both auth paths stay in sync if constants change.

Add 16 new tests: grace period boundary precision, base62 charset
validation, current+previous token during grace, stopTokenRotation
state cleanup, rate limit reset, consumed token eviction, full
end-to-end QR flow, per-IP 429, cookie attributes, concurrent race,
regenerate invalidation, URL encoding, path traversal, /q without
param, and session record method:qr.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:32:02 +01:00
arkonandClaude Opus 4.6 97ca13916a refactor: consolidate ~70 scattered constants into 6 new config files (phase 6)
Create 6 domain-focused config files in src/config/ to centralize operational
tuning knobs that were scattered across 15+ source files:

- server-timing.ts: SSE batching, state debounce, scheduled runs, error recovery
- auth-config.ts: session TTL, rate limits, hook timeout
- tunnel-config.ts: QR token rotation, tunnel process lifecycle
- terminal-limits.ts: input length, terminal cols/rows, session name
- ai-defaults.ts: AI model string, idle/plan context limits
- team-config.ts: poll interval, cache sizes

Eliminates all cross-file duplicates:
- AI model string: 5 occurrences → 1 (config only)
- STATS_COLLECTION_INTERVAL_MS: 2 → 1
- timeout: 10000 in hooks-config: 6 → 0 (now HOOK_TIMEOUT_MS)
- MAX_TRACKED_AGENTS shadow: removed, imports from map-limits

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 13:19:16 +01:00
arkonandClaude Opus 4.6 e0a2774d37 perf: implement phase 1-3 performance optimizations
Add implementation plans and code structure analysis for a 3-phase
performance optimization effort. Refactor core modules to reduce
timer overhead, consolidate regex usage, extract exec timeout config,
add debouncer utility, and streamline server/schema validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 17:39:44 +01:00
arkonandClaude Opus 4.6 545913790b chore: remove dead code and unused exports (-159 lines)
Remove confirmed-unused code found via team-based codebase analysis:
- Dead interfaces: SessionResponse (types.ts), PlannerResult (plan-orchestrator.ts)
- Dead functions: withTimeout (session.ts), getOpenCodeAugmentedPath, resetOpenCodeCache (opencode-cli-resolver.ts)
- Dead config constants: MAX_RUN_SUMMARY_EVENTS, TRIM_RUN_SUMMARY_TO, MAX_MESSAGES_PER_CHANNEL (buffer-limits.ts), MAX_TRACKED_AGENTS, MAX_SUBAGENT_ACTIVITY_PER_AGENT, MAX_TOOL_RESULTS_PER_AGENT, COMPLETED_TODO_TTL_MS (map-limits.ts)
- Dead legacy code: idleTimer property + clearIdleTimer() no-op method + 6 call sites (respawn-controller.ts)
- Stale barrel re-exports: createAnsiPatternFull/Simple, validateTokenCounts/Cost, stripAnsi, normalizePhrase, getOpenCodeAugmentedPath (utils/index.ts)
- Un-exported internal-only symbols: ErrorMessages (types.ts), destroyRalphLoop (ralph-loop.ts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 02:01:21 +01:00
arkonandClaude Opus 4.6 8fc0dc8c2e chore: bump version to 0.1655
Security hardening: timing-safe auth comparison, localhost-only hook bypass,
SSE client limit, TLS key permissions, strict settings schema, logout endpoint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 01:13:58 +01:00
arkonandClaude Opus 4.6 3795c45cc1 chore: rename Claudeman to Codeman
Full product rename across 109 files (~834 occurrences):
- Env vars: CLAUDEMAN_* → CODEMAN_*
- Data dirs: ~/.claudeman/ → ~/.codeman/, ~/claudeman-cases/ → ~/codeman-cases/
- tmux prefix: claudeman- → codeman-
- localStorage: claudeman-* → codeman-*
- Package/CLI: claudeman → codeman
- GitHub repo: Ark0N/Claudeman → Ark0N/Codeman
- systemd service: claudeman-web → codeman-web
- Class: ClaudemanApp → CodemanApp

Migration infrastructure for seamless transition:
- state-store.ts: auto-migrates data directories on startup
- tmux-manager.ts: dual-prefix detection (legacy claudeman- sessions)
- app.js: localStorage key migration (preserves old keys)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 16:44:34 +01:00
arkon 926e77b1c2 chore: bump version to 0.1550 2026-02-19 08:49:36 +01:00
arkonandClaude Opus 4.5 a92826b66d refactor: simplify ralph wizard from 9 agents to 2
Remove execution layer that never actually controlled execution:
- execution-bridge.ts (model param was ignored)
- group-scheduler.ts (task-tool mode never used)
- model-selector.ts (recommendations were display-only)
- context-manager.ts (never invoked)
- execution-limits.ts

Remove redundant agent prompts (overlapping outputs):
- requirements-analyst, architecture-planner, risk-analyst
- testing-specialist, verification (merged into planner.ts)
- execution-optimizer (output was ignored)
- final-review (scores were cosmetic)

Simplify plan-orchestrator.ts from 2400 LOC to 520 LOC:
- Before: 9 agents, 6 phases, ~40-60 minutes
- After: 2 agents (research + planner), ~18 minutes

Remove /api/execution/* endpoints and related server code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 12:15:09 +01:00
arkonandClaude Opus 4.5 55e9f9f0a5 feat(execution): add ExecutionBridge for parallel task execution
Implements the Execution Optimizer Rework plan that enables the system
to actually use the optimizer metadata (parallelGroups, agentType,
recommendedModel, requiresFreshContext, estimatedTokens) that was
previously generated but ignored.

New components:
- ExecutionBridge: Central coordinator that loads optimized plans,
  manages parallel execution within groups, and coordinates with
  SpawnOrchestrator for session-based execution
- ModelSelector: Routes tasks to appropriate models (opus/sonnet/haiku)
  based on user defaults and agent type overrides. Optimizer
  recommendations are advisory only - user preferences always win
- GroupScheduler: Builds topologically ordered execution groups,
  manages dependencies, determines execution mode (session vs task-tool)
- ContextManager: Handles fresh context requirements via /clear+/init
  or new session spawning

Features:
- Parallel task execution within groups (configurable limit)
- Group-level dependency tracking (lower groups complete first)
- Partial failure handling (continue with non-dependent tasks)
- Model configuration in App Settings > Models tab
- Agent type overrides (explore, implement, test, review)
- Execution control API endpoints (start, pause, resume, cancel)
- SSE events for real-time execution progress visibility
- Execution history tracking

API endpoints:
- GET/POST/PUT /api/execution/* for execution control
- GET/PUT /api/execution/model-config for model settings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 07:07:18 +01:00
arkonandClaude Opus 4.5 83e5b0b78e feat: add resource management types and utilities for memory optimization
- Add Disposable, BufferConfig, MemoryMetrics, CleanupRegistration types
- Create src/config/buffer-limits.ts with consolidated buffer size constants
- Create src/config/map-limits.ts with Map size limits to prevent unbounded growth
- Implement BufferAccumulator utility with configurable trim and onTrim callback
- Implement LRUMap with automatic eviction and O(1) operations
- Implement CleanupManager for unified resource cleanup with isStopped guard
- Add comprehensive tests for all new utilities

This lays the foundation for memory leak prevention and performance improvements.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 04:35:20 +01:00