mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
3bd19956ffeb45c6df1d5169811922847e22910c
18
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
60930f0ba4 |
fix(tui): credential-isolated env-token auth (ends recurring 401) + reap defunct sessions (#141)
* fix(tui): pass CLAUDE_CODE_OAUTH_TOKEN to spawned claude + reap defunct sessions
Root cause (PI231 incident): tmux does not forward the parent's env to the
pane, so the TUI claude never saw CLAUDE_CODE_OAUTH_TOKEN and fell back to
~/.claude/.credentials.json, whose single-use refresh token got corrupted to an
empty string by the per-request spawn + kill-session teardown racing claude's
token rotation -> permanent "Please run /login" 401 (re-login re-corrupted on
the next spawn). Connected leak: the pane's claude is a child of the tmux server
(not node), so kill-session left <defunct> zombies the server never reaped (25
over 30 days; tmux kill-server dropped it 25->3).
Fix 1: buildTuiCmd now adds CLAUDE_CODE_OAUTH_TOKEN=<shq-escaped> to the pane
env prefix when the env is set, so claude authenticates via the long-lived token
and never touches the credentials.json refresh path (matching stable hosts).
Unset -> no token added (credentials.json-only hosts unaffected).
Fix 2: reapStaleTuiSessions kill-servers after clearing our own sessions ONLY
when no foreign tmux session remains (never disrupts a co-hosted olp-tui-*).
kill-server is the only node-reachable action that ACTUALLY reaps -- server exit
reparents survivors to init, which waitpids them; a per-session kill cannot,
since node is not the zombies' parent. Added a 15-min periodic reap (server.mjs)
gated on TUI_MODE and on the TUI path being idle. Residual: a request whose pane
is created in the idle-check/kill-server window fails cleanly via the existing
honesty gates (documented).
ALIGNMENT: Class B (OCP-owned TUI spawn). cli.js does NOT perform either
operation -- there is no cli.js analogue for "how the TUI pane authenticates" or
"reaping tmux-server-owned zombies"; authorized by ADR 0007 (PR-C amendment) per
ALIGNMENT.md's Class B citation requirement. No Class A wire surface, no endpoint
shape, no alignment.yml token, and no models.json entry touched.
Tests: +6 in test-features.mjs (buildTuiCmd token set/unset/shq-injection;
reaper kill-server ours-only / foreign-present / no-server). 241 passed, 0 failed.
Co-Authored-By: Claude <claude-opus> <noreply@anthropic.com>
* fix(tui): isolate TUI auth to env-token-only home (no credentials.json shadowing)
Passing CLAUDE_CODE_OAUTH_TOKEN to the spawned interactive `claude` (commit
|
||
|
|
3322d7bdae |
feat(tui): per-path concurrency limit + /health observability (PR-B) (#139)
Two P1 audit fixes for the TUI subscription-pool bridge (ADR 0007). The
default path (CLAUDE_TUI_MODE unset) is unchanged except the additive
/health `tui` block (enabled:false when off).
C-4 — TUI path had NO concurrency bound. The global MAX_CONCURRENT gate
lives in spawnClaudeProcess (the -p/stream-json path); callClaudeTui never
calls it — it calls runTuiTurn, which cold-boots a full interactive claude
in tmux. So N concurrent TUI requests spawned N simultaneous cold boots (a
family burst of 5 on a Pi 4 = OOM risk + subscription rate-limit pressure).
Adds an independent in-process limiter (lib/tui/semaphore.mjs, TuiSemaphore)
gating callClaudeTui: OCP_TUI_MAX_CONCURRENT (default 2 — a TUI turn is heavy:
per-request cold-boot + up to 120s wallclock). Queues rather than rejects
(mirrors MAX_CONCURRENT intent), with a bounded wait queue (default 32x the
limit) → tui_queue_full (503) on overflow rather than unbounded growth. The
slot releases in a finally, so PR-A's honesty-gate throws / timeouts / paste
failures never leak a slot.
C-5 — no operator-visible TUI drift surface. The tui_entrypoint_mismatch
warning only reached journald; after the 6/15 flip a silent sdk-cli drift
(the documented top risk) would drain metered credits invisibly. Adds an
additive `tui` block to /health: { enabled, entrypointMode, lastEntrypoint,
entrypointMismatches, inflight, queued, maxConcurrent }. lastEntrypoint /
entrypointMismatches are recorded in callClaudeTui (same mismatch branch the
journald warning covers); inflight/queued come from the C-4 semaphore.
ALIGNMENT (Class B): cli.js does NOT perform this operation — both the TUI
path and /health are OCP-owned, so no cli.js citation applies. /health is a
grandfathered B.2 endpoint (ADR 0006, frozen at v3.16.4). The response-shape
change is authorized by the ADR 0007 PR-B amendment added in this commit and
is behaviour-preserving: the `tui` block is NEW fields only — no existing
/health field is changed, renamed, removed, or re-typed, and no existing
semantics change, so existing consumers (dashboard, ocp-connect, monitoring)
are unaffected. Per ALIGNMENT.md's grandfather provision, an additive
behaviour-preserving change to a grandfathered B.2 endpoint is authorized by
an ADR; ADR 0007 is the authority for the TUI observability surface. No
Class A forwarding path, no alignment.yml, no models.json touched — alignment
blacklist is unaffected (zero new network tokens).
Tests: 11 new (lib/tui/semaphore.mjs is importable, so the semaphore + the
two pure /health helpers are tested directly): limit=1 serializes two
overlapping calls; limit=2 runs two + queues the third; slot released on
throw; bounded queue → tui_queue_full; mismatch counter increments on
cli→other drift; auto mode never counts a mismatch; /health tui block shape
+ live inflight/queued. npm test: 235 passed, 0 failed (was 224).
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude <claude-opus> <noreply@anthropic.com>
|
||
|
|
79c1d61e1d |
docs(tui): 6/15 canary + flip/rollback runbooks + setup auth-probe guard (PR-C) (#138)
Docs scope (no server.mjs touched): - docs/runbooks/615-canary.md — executable canary runbook: quiesce host, read Agent SDK credit balance manually (no programmatic API exists for that pool — this is stated explicitly to avoid endpoint hallucination), send one Haiku turn via TUI-mode, confirm cc_entrypoint=cli in transcript, re-read balance, green/red decision tree, periodic self-classification mini-canary with OCP_TUI_ENTRYPOINT=auto. - docs/runbooks/tui-flip-rollback.md — flip and rollback procedure for systemd (EnvironmentFile + daemon-reload) and launchd (plist edit + bootout/bootstrap cycle). Calls out both known pitfalls explicitly: daemon-reload required on systemd; launchctl kickstart -k does NOT reload plist env on launchd (matches MEMORY.md operational pit entry). - README.md § "2026-06-15 operator checklist" — brief fleet checklist + pointers to both runbooks. Placed inside the existing TUI-mode section just before "Architecture and design decisions". - README.md § Environment Variables — adds OCP_SKIP_AUTH_TEST row. Operator-tooling scope (setup.mjs, not a Class A wire path): - setup.mjs auth quick-test: wraps the existing claude -p probe in an OCP_SKIP_AUTH_TEST=1 gate; adds inline comment warning that after 2026-06-15 the probe draws from the Agent SDK credit pool. The setup flow is unchanged when OCP_SKIP_AUTH_TEST is unset. Alignment note: this PR does not touch server.mjs. The setup.mjs change is operator-tooling only — it guards a local test spawn, not any wire path. No cli.js citation required (no Class A surface is modified). Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude claude-sonnet-4-6 <noreply@anthropic.com> |
||
|
|
cd98b51b96 |
docs(plans): add anthropic-only sandbox strategy handoff (#102)
Forward-looking planning doc capturing prior-art analysis from OLP's Phase 7 PR-B re-evaluation, scoped down to OCP's single-provider (anthropic) deployment. Documents: - Multi-tenant gap for OCP (cross-key lateral read, OAuth exposure) - Why OLP's outer-bwrap PR-B approach is the wrong path to copy (Anthropic design intent, ~/.claude.json upstream not-planned) - Three viable alternatives: A. Ephemeral $HOME via env var (~50 LOC, recommended Phase 1) B. bwrap --tmpfs $HOME + ro-bind credentials (apt dep, Linux only) C. OverlayFS lowerdir+upperdir (needs CAP_SYS_ADMIN) - Orthogonal cross-key isolation layer (per-spawn customConfig denyRead or per-OS-user spawning) - Trust-tier framing (single-user / family-zone / shared-host) Not an ADR — becomes one only when work actually starts. Not binding; ALIGNMENT.md authority requirements still apply when sandbox code lands. Cross-references OLP's parallel multi-provider work at dtzp555-max/olp ADR 0014 Amendment 1 (pending) and archive branch phase-7-pr-b-outer-bwrap-snapshot. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
74260d7f6f |
feat(tui): opt-in CLAUDE_TUI_MODE — interactive subscription-pool bridge (PR-1..PR-4 squashed) (#104)
Reader + driver + home modes + server wiring + entrypoint hardening. 5 independent reviews folded; e2e green; default stream-json path byte-identical when flag off. See PR description + ADR 0007 for the full layer/authority/security detail. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1dd6fb9440 |
docs(governance): add ADR 0006 OpenAI shim scope + Class A/B taxonomy (#100)
Introduces an explicit two-class taxonomy of OCP endpoints to resolve the structural ambiguity surfaced by PR #99 (external response_format honoring on /v1/chat/completions): - Class A: cli.js-mirror endpoints. Rules 1-5 of ALIGNMENT.md apply verbatim. Citation requirement unchanged (cli.js:NNNN). The 2026-04-11 drift discipline is preserved without weakening. - Class B: OCP-owned compatibility endpoints. Anchored to OpenAI's /v1/chat/completions specification (B.1) or to an authorizing ADR (B.2). Citation shifts to spec section + ADR number. Class B inherits the same anti-invention discipline; the anchor differs. Grandfather provision in ADR 0006 retroactively authorizes the 12 existing B.2 administrative endpoints at v3.16.4 behaviour (one-time, contract-frozen). New B.2 endpoints or any new method on a grandfathered endpoint requires its own ADR per Rule 4 (Class B mapping). Changes: - new: docs/adr/0006-openai-shim-scope.md - new: docs/openai-compat-pin.md (placeholder for first B.1 audit) - mod: ALIGNMENT.md (Class B section, rule mapping table, updated Unalignable Policy and Annual Audit scope; Rules 1-5 byte-identical) - mod: .github/PULL_REQUEST_TEMPLATE.md (Endpoint Class radio, separate evidence sections for A vs B, reviewer checklist updated) - mod: docs/adr/README.md (index entry for ADR 0006) Independent reviewer (fresh-context opus per Iron Rule 10) verified: all 12 load-bearing checks pass — Rules 1-5 byte-identical to origin/main, 2026-04-11 drift narrative unchanged, explicit non-relitigation safeguard present in ADR 0006, grandfather provision narrowly scoped, Class B inventory matches server.mjs reality (14/14), single governance layer per Iron Rule 11 (no server.mjs touch), alignment.yml unmodified. Verdict: APPROVE_WITH_MINOR with 3 of 5 non-blocking suggestions folded in (operations-vs-endpoints precision, PR template Hybrid wording, openai-compat-pin.md stub). Triggering incident: PR #99 by external contributor (response_format honoring on /v1/chat/completions). This governance PR ships separately per Iron Rule 11 (IDR); the feature PR #99 will rebase on this and declare Class B with ADR 0006 citation. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
d245c62df7 |
docs(images): replace dashboard.png with multi-client + multi-session traffic (#84)
Continues #82 / #83 — those captures had stats counters at 0 / 3 respectively, with 0 active sessions. The Sessions card therefore visually undermined the rest of the dashboard. Re-captured after deliberately seeding LAN-side traffic from both Pi231 and MacBook clients (each holding a per-key API token issued on the Mac mini server), mixing single-turn and multi-turn requests with distinct session_id values to populate the in-memory sessions Map. New screenshot data points: - Uptime: 21h 56m - Requests: 24 / 0 active (up from 3 / 0) - Errors: 0 / 0 timeouts - Sessions: 8 active (was 0) - Plan Usage: 5h 20%, weekly 28% - byKey table: 11 keys with rich history, now including pi231-test + macbook-test rows from this round - Recent Requests: visible row count grown Multi-turn correctness was incidentally verified during seeding — a Pi231 request with session_id=pi-multi-01 turn 2 correctly recalled the number passed in turn 1 ("the number 7"), confirming session state persists across cli.js subprocess turns as designed. Capture method unchanged (Chrome headless, 1400x2400, --virtual-time-budget=14000). Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js citation) does not apply. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
047750e642 |
docs(images): replace dashboard.png with stats-populated version (#83)
#82 captured the dashboard mid-idle — server had been up 21h with zero traffic since restart, so the in-memory stats counters all showed 0 (Requests 0 / Errors 0 / Sessions 0). That made the top strip of the screenshot look like a dead service even though the historical byKey + Plan Usage data below were healthy. Re-captured after firing 3 small haiku requests through localhost to populate stats.totalRequests. The new screenshot now shows: - Status: ok / v3.13.0 - Uptime: 21h 39m (up from 21h 28m) - Requests: 3 / 0 active (was 0 / 0) - Plan Usage: 5h 17%, weekly 28% (was 13% / 27%) - Recent Requests: 2 visible rows showing model + latency + status (was empty) Same capture method as #82: Chrome headless --window-size=1400,2400 --virtual-time-budget=12000. The 3 priming requests were short haiku prompts ("reply with the single word OKn"), max_tokens=12 each. Plan-usage delta < 1%, byKey table already had 11 active keys with rich history so the priming did not distort the long-tail data. This addresses feedback that the previous screenshot's empty stats strip undermined the rest of the dashboard's data richness for first- time README readers. Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js citation) does not apply. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
3bdeb50ed5 |
docs(images): refresh dashboard.png with current prod data (#82)
The previous dashboard.png was captured on day-1, before any real traffic — the screenshot was effectively empty (no Plan Usage bars, single API key, no Recent Requests). It made the Web Dashboard look like a placeholder rather than a working observability surface. Replaced with a fresh capture of the maintainer's Mac mini production OCP (running v3.13.0, multi auth) showing: - Status / uptime / active+queued counters - Plan Usage bars (5h: 13%, weekly: 27%) — real subscription draw - Usage by Key table — 11 active keys with request counts, success/error rates, average latency, last-seen timestamps - API Keys section — all 32 registered keys with truncated key prefixes + creation date + active/revoked status (truncation matches existing dashboard rendering, no full keys are exposed) - Recent Requests log — request stream with model, prompt size, latency Capture method: Chrome headless (no Playwright extension required) at 1400x2400, --virtual-time-budget=12000 to let dashboard JS finish fetching + rendering before the screenshot frame. PNG dimensions: 1400 x 2400 (was 1400 x 1739). The added height comes from the now-populated Usage by Key + API Keys + Recent Requests sections — not from layout changes. Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js citation) does not apply. Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
16eeb66557 |
feat(cache): per-key isolation, cache_control bypass, chunked stream replay (#65)
* docs(governance): ADR 0005 (no multi-provider) + cache upgrade spec Governance prelude for the cache upgrade work: - docs/adr/0005-no-multi-provider.md — locks in the decision that OCP stays single-provider (Anthropic via cli.js spawn). Cache improvements are explicitly in scope (decision §3); multi-provider refactor is explicitly out of scope, with three documented trigger conditions for revisiting. - docs/adr/README.md — index updated to reference 0005. - docs/superpowers/specs/2026-05-07-cache-upgrade-design.md — design for the cache upgrade work split into PR-A (per-key isolation, cache_control bypass, chunked stream replay) and PR-B (singleflight stampede protection). Each design decision has a written rationale. server.mjs is not modified; this commit is doc-only and therefore exempt from the cli.js citation requirement (ALIGNMENT.md Rule 5 applies only to commits that touch server.mjs). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(cache): per-key isolation, cache_control bypass, chunked stream replay cli.js does not perform response caching at the proxy layer. OCP's response cache is a value-add operation internal to OCP, between the wire and the cli.js spawn. It does not introduce, rename, or alter any endpoint, header, request field, or response field that cli.js emits or expects — this change qualifies under ALIGNMENT.md Rule 2's value-add carve-out for non-wire- affecting proxy operations. no client-observable wire shape change. Spec: docs/superpowers/specs/2026-05-07-cache-upgrade-design.md D1 — Per-key cache isolation (cacheHash v2 format) keys.mjs: prepend `v2|k:<keyId or "anon">|` before existing hash fields. Backward-compatible: absent/null/empty keyId folds to "anon". v1-format rows in response_cache are abandoned naturally; TTL cleanup at server.mjs:185 reaps them within one window. No migration needed. server.mjs: pass keyId: req._authKeyId at the single cacheHash call site (line ~1221). D2 — cache_control bypass keys.mjs: export hasCacheControl(messages) — walks messages and nested content arrays for presence of cache_control field. server.mjs: if hasCacheControl(messages) is true, set req._cacheHash = null and log cache_skipped{reason: cache_control_present}; existing `if (CACHE_TTL > 0 && req._cacheHash)` guards on write-back handle the skip. D3 — Chunked stream replay (80 codepoints/chunk, no artificial delay) server.mjs: replace single-chunk cached.response emission with an Array.from(cached.response) loop in steps of CACHE_REPLAY_CHUNK_SIZE=80. Array.from ensures multibyte UTF-8 codepoints (e.g. CJK) are never split. Tests: 12 new cases in test-features.mjs (36 total, 0 failed). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
313cb13a78 |
docs: align README + governance docs with current state (uninstall, files, ADR index, ship-archive) (#59)
Multiple documentation polish items rolled into one PR (one layer: "docs alignment with current state"). ### README.md - **Uninstall section** added between Server Setup and Client Setup (was missing — `node uninstall.mjs` exists but went undocumented). - **OpenClaw definition** added as a footnote on first README mention (the architecture diagram and Supported Tools table both reference OpenClaw without ever defining it). - **Repository Layout section** added before Security — table of top-level files (server.mjs, setup.mjs, uninstall.mjs, keys.mjs, models.json, ocp/ocp-connect, dashboard.html, scripts/, .claude/skills/, ocp-plugin/, docs/adr/, ALIGNMENT.md, AGENTS.md, CLAUDE.md) so a new contributor knows what each file does. - **LICENSE link** added to the License section footer. ### docs/adr/README.md (new) - Index of the three published ADRs (0002, 0003, 0004) with a one-line description each. - Explains the `0001` placeholder (early internal proposal that was superseded; numbering deliberately starts at `0002`). - Guidance on when to write a new ADR vs. when a commit message suffices. ### Spec/plan housekeeping - `specs/.gitkeep` removed (the empty `specs/` placeholder confused the picture; canonical paths are `docs/superpowers/plans/` for active plans and `docs/superpowers/specs/` for long-lived design docs that other code references). - Shipped plans moved to `docs/superpowers/plans/shipped/`: - `2026-04-10-lan-mode.md` (shipped: README LAN mode section) - `2026-04-25-47-sse-heartbeat-plan.md` (shipped: v3.12.0 per CHANGELOG) - `2026-04-25-47-sse-heartbeat-design.md` left in `docs/superpowers/specs/` unchanged because both `server.mjs:565` and `CHANGELOG.md:7` link to that exact path; moving it would require a `server.mjs` edit, which needs `cli.js` citation per ALIGNMENT.md Rule 1. ### AGENTS.md - Updated "Key files to know" to add `docs/adr/README.md`, `docs/superpowers/plans/`, and `memory/constitution.md`. - Note explaining `memory/constitution.md` is spec-kit's standard location, distinct from `~/.cc-rules/memory/` and `ALIGNMENT.md`. - Updated "Handoff expectations" item 5 from `docs/superpowers/specs/*/tasks.md` (which never matched anything — there were no `tasks.md` files there) to `docs/superpowers/plans/` (excluding `shipped/`). ### Coordination with PR #53 PR #53 is open and adds "Why OCP?", "Comparison", and "Governance" sections to README. This PR deliberately avoids those areas — only edits the Supported Tools table footnote, inserts Uninstall before Client Setup, inserts Repository Layout before Security, and updates the License footer. No expected merge conflict. Refs: audit findings M6, M8, M9, M11, L2, L3, L6. Co-authored-by: dtzp555 <dtzp555@gmail.com> |
||
|
|
b871b72b6b |
feat(server): SSE heartbeat on streaming path (#47) (#49)
* docs(spec): design for #47 SSE heartbeat on streaming path Draft spec for an opt-in idle-watchdog SSE heartbeat covering both pre-first-byte and mid-stream silent windows. Default disabled, controlled by CLAUDE_HEARTBEAT_INTERVAL. Targets ~40 LOC. Decisions captured: D1 whole-stream reset-on-byte; D2 SSE comment frame; D3 default disabled; D4 relocate ensureHeaders() to post-spawn; D5 X-Accel-Buffering: no on both SSE header sites; D6 single log line per affected request. Scope-locked: does not touch CLAUDE_TIMEOUT semantics, the separate server.mjs:480-489 dangling-client bug, issues #41/#42, or the non-streaming path. Includes privacy preflight and cloud-testing plan per maintainer feedback. Refs: #47 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(plan): implementation plan for #47 SSE heartbeat 6 phases: pre-work (file 480-bug), implementation (5 tasks on feat/47-sse-heartbeat), opus fresh-context review, cloud verification on Mac rig, privacy preflight, PR+release. Each implementation task carries concrete code, syntax check, and a scoped commit message. LOC budget enforced in Task 1.6 gate (~45 server.mjs lines max). Reviewer checklist scopes scope-lock, ALIGNMENT, privacy, and heartbeat-cannot-abort discipline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(server): add startHeartbeat helper + HEARTBEAT_INTERVAL env var Per design doc (refs #47). Helper is a per-request idle watchdog that emits `: keepalive\n\n` SSE comment frames; returns a {reset, stop} handle. No wiring yet — helper is unused, safe to commit in isolation. cli.js citation: N/A — SSE response shaping is an OCP-owned translation layer, not a cli.js operation. See AGENTS.md and ALIGNMENT.md Rule 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(server): eagerly send SSE headers post-spawn (D4, refs #47) Moves the ensureHeaders() call from "on first stdout byte" to "immediately after successful spawn." This is a prerequisite for the heartbeat covering the pre-first-byte silent window (the 'processing large contexts' failure mode in #47). Behavioral consequence: the narrow "spawn succeeded but subprocess died before any byte" branch at server.mjs:610-611 becomes effectively dead in the common case. The post-headers SSE-stop path (612-619) handles it instead. The branch remains defensively for the client-closed-before- ensureHeaders race. Isolated commit per design doc §D4 so reviewer can focus on this one behavior change. cli.js citation: N/A — SSE header emission is OCP response-shaping. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(server): wire heartbeat into streaming path (refs #47) - sendSSE() accepts optional hb handle and calls hb.reset() before write - callClaudeStreaming starts heartbeat after ensureHeaders() and passes hb to the three streaming sendSSE call sites - All three exit paths (proc close, proc error, res close) call hb.stop() to guarantee timer cleanup; no-op handle when disabled means zero runtime cost when CLAUDE_HEARTBEAT_INTERVAL=0 Heartbeat never aborts — only writes comment frames and re-arms. Aligns with v3.3 timeout discipline (single CLAUDE_TIMEOUT, no secondary client-killing timers). cli.js citation: N/A — SSE response shaping is OCP translation layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(server): add X-Accel-Buffering: no to SSE response headers (refs #47) nginx (and many LBs / Cloudflare) default to proxy_buffering=on, which would buffer heartbeat comment frames indefinitely and defeat the feature silently. This header hints no-buffering; other stacks ignore it. Applied at both SSE header sites (real streaming + cache-hit). cli.js citation: N/A — response header shaping is OCP translation layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(release): v3.12.0 — streaming heartbeat (refs #47) Bundles the release-kit companion files per Iron Rule 5.2 / 11 example: version bump across package.json + ocp-plugin + openclaw.plugin.json, CHANGELOG v3.12.0 section, README env var row + "Streaming heartbeat" explainer. Tag push to v3.12.0 triggers .github/workflows/release.yml to create the GitHub Release automatically. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(server): ensureHeaders returns true when already sent (refs #47) Phase 3 smoke test revealed every content chunk was being dropped after the D4 eager ensureHeaders() call: the stdout.on('data') guard `if (!ensureHeaders()) return;` early-returned on every chunk because ensureHeaders returned false for the already-sent case (conflated with the dead-connection case). Split the two conditions explicitly: return false only when res is ended/destroyed; return true when headers are (already or now) sent. This also fixes a latent multi-chunk bug on main that was masked because claude CLI typically outputs in one stdout chunk. Verified: node -c server.mjs; subsequent re-run of Phase 3 smoke test now sees streaming content chunks + heartbeat frames. cli.js citation: N/A — SSE response shaping is OCP translation layer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1c29f4867f |
chore(privacy): scrub personal identifiers from public repo (#43)
Removes all occurrences of maintainer's real name and handle from tracked files in this PUBLIC repository. Replaces with generic terms like "project maintainer" / "the maintainer". Files scrubbed: - CLAUDE.md (2 instances) - ALIGNMENT.md (Auditor field) - docs/adr/0002, 0003, 0004 (Authors/Deciders fields + body text) - memory/constitution.md (1 instance) - docs/superpowers/plans/2026-04-10-lan-mode.md (Users/taodeng paths → $HOME) Also: - Replaces specific machine hostnames (Taos-Mac-mini, Taos-MacBook-Pro) with role-based names (home-mac, travel-macbook) in ADR 0001 (if committed; currently untracked). - Replaces /Users/taodeng/ paths with $HOME/ in old plan doc. NOTE: git history still contains the personal info. See postmortem (issue TBD) for whether to rewrite history via git-filter-repo. Co-authored-by: Tao Deng <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
2ecc4945ad |
docs(governance): add AGENTS.md bridge + 3 historical ADRs (#36)
Integrates OCP with Tao's cross-device development system (Phase 1 L1). - AGENTS.md: project-level tool-agnostic instructions (read by Cursor, OpenCode, Copilot, etc. in addition to Claude Code). Inherits from ~/.cc-rules/AGENTS.md. - CLAUDE.md: prepends @AGENTS.md + @~/.cc-rules/AGENTS.md imports. - docs/adr/0002-alignment-constitution.md: captures why ALIGNMENT.md exists (2026-04-11 drift response). - docs/adr/0003-models-json-spot.md: captures v3.11.0 SPOT refactor. - docs/adr/0004-openclaw-auto-sync.md: captures v3.11.0 auto-sync design. ADRs numbered 0002-0004 because an untracked 0001-cross-device-system.md already exists locally and was out of scope for this PR. Governance/docs only. No code change. No version bump (not a release). Co-authored-by: Tao Deng <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
9f1a21f7ad |
docs: update dashboard screenshot with live usage data
Session 53%, Weekly 40% — proper colors via Playwright Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
a0f9268af5 |
fix: dashboard token via URL param + correct screenshot
- Support ?token=xxx query parameter for dashboard auth (enables headless browser screenshots and direct links) - Token is saved to localStorage and URL is cleaned up - Fix pathname matching to handle query parameters in URL - Replace html2canvas screenshot with Playwright (accurate colors) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
087e26346f |
feat: add ocp-connect lightweight client + restructure README
- Add standalone `ocp-connect` script for zero-dependency client setup (only needs curl + python3, no Node/repo clone required) - Add `ocp connect` command to main CLI - Add `authMode` field to /health endpoint - Restructure README with clear Server Setup / Client Setup sections - Add dashboard screenshot to README - Fix smoke test model name (claude-haiku-4-5-20251001) - Fix auth mode detection for shared/multi/none - Fix rc file cleanup idempotency (blank line handling) - Fix key input echo (now hidden with read -rs) - Add host validation - Bump version to 3.5.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
3e8ff7a509 |
docs: add LAN mode implementation plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |