Commit Graph
65 Commits
Author SHA1 Message Date
6d4751f983 feat(tui): OCP_TUI_FULL_TOOLS gate — -p-equivalent tools for single-user TUI (#135)
* feat(tui): OCP_TUI_FULL_TOOLS gate — -p-equivalent tool surface for single-user TUI

Lets a SINGLE-USER / trusted TUI deployment run a tool-using / MCP agent (e.g. an
OpenClaw assistant) on the subscription pool. When OCP_TUI_FULL_TOOLS=1, buildTuiCmd
grants the interactive session the SAME tool surface as the -p A-path — --allowedTools
(+ optional --mcp-config / --dangerously-skip-permissions, read from the same
CLAUDE_ALLOWED_TOOLS / CLAUDE_MCP_CONFIG / CLAUDE_SKIP_PERMISSIONS env as buildCliArgs)
— instead of the default MCP-walled, built-in-tools-only set.

Motivation: the default TUI tool wall (--strict-mcp-config --disallowedTools mcp__*)
exists for multi-tenant safety, but it also blocks a trusted single-operator agent from
its MCP tools — forcing tool-using agents onto the metered -p pool after 2026-06-15.
This gate resolves that for the single-user case.

Safe to gate ON only because TUI is hard-incompatible with AUTH_MODE=multi (server.mjs
refuses to boot, see existing guard), so this can NEVER widen a guest's tool surface.
Default (gate unset) is unchanged: MCP wall + built-in tools only.

Verified live on PI231 through the real OCP TUI path (spike 2026-06-02):
- built-in tools: claude created + read a file on the host (2 tool_use entries);
- MCP: claude invoked a custom MCP server's tool (mcp__spike__echo_marker) and returned
  its output;
- billing: entrypoint=cli (subscription pool) WITH full tools + MCP;
- completion: end_turn, no permission stall, no hang.

Tests: +1 (full-tools branch: --allowedTools present + MCP wall dropped; skip-permissions
supersedes; mcp-config threaded). 195 pass. README: new OCP_TUI_FULL_TOOLS env var entry.

ALIGNMENT.md: changes lib/tui/session.mjs, not server.mjs — server.mjs hard requirements
do not trigger. Mirrors buildCliArgs() permissions logic; the flags are documented Claude
Code CLI flags, not invented endpoints. cli.js citation N/A under Rule 2. ADR 0007 (A-path).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(tui): shq operator tool tokens in full-tools shell string (reviewer Finding 2)

Independent reviewer (APPROVE WITH CHANGES) caught that buildTuiCmd returns a SHELL
STRING (run by tmux via sh -c), unlike buildCliArgs which returns an argv array to
spawn(). Operator-supplied CLAUDE_ALLOWED_TOOLS can be a scoped specifier such as
"Bash(npm run test:*)" or "Read(~/**)", whose ( ) * ~ would break / inject the shell
command if pasted bare. Now shq() each allowed-tool token (operator-self-injection only —
guests cannot reach TUI per the multi-mode boot guard, but it's a real correctness bug).

Also: tightened the README OCP_TUI_FULL_TOOLS wording per reviewer Finding 1 — the precise
safety property is "no guest key can reach the TUI path (multi-mode boot is a hard exit)",
and noted the AUTH_MODE=shared + OCP_TUI_ALLOW_LAN trust model is unchanged.

Test: +scoped-specifier assertions (token shq'd; never appears unquoted). 195 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 11:03:20 +10:00
0dced52215 chore(release): v3.19.0 — TUI large-prompt reliability (#130) + proxy-purity context (#4) (#134)
Release prep only: bump package.json 3.18.0 → 3.19.0, CHANGELOG entry, README TUI
"what changes / what doesn't" note that the host CLAUDE.md/auto-memory is never injected.

Bundles two already-merged, independently-reviewed TUI fixes:
- #130 (PR #131): reliable large multi-line paste + version-robust turn detection.
- #4   (PR #132): never inject host CLAUDE.md / auto-memory into proxied turns.

Both verified live on PI231 + Oracle; adversarial multi-host battery passed
(0 hangs / 0 crashes / 0 injection / 0 leaks). Follow-up: #133.

No server.mjs change in this PR (package.json + CHANGELOG + README only) → cli.js
citation N/A. release_kit (Iron Rule 5.5): version_source=package.json, changelog,
README docs all updated; no new env var / endpoint / subcommand.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 07:46:55 +10:00
c3b1f32c86 fix: error-output sanitization + process-lifecycle hardening (#111) (#118)
Four findings from the 2026-05-31 audit (3×P2 + 1×P3), all in the error/process
lifecycle layer:

1. sanitizeError() helper — streaming error paths sent raw claude error_message /
   stderr to the client, leaking home-dir / credential-file paths that the
   non-streaming path already redacted. Factored the path-strip regex into one
   helper and applied it at all 9 client-facing jsonResponse/sendSSE error emits;
   de-duped the 3 pre-existing inline .replace() sites. Operator-log calls
   (logEvent/trackError) and admin-gated endpoints left raw by design.

2. res.on("close") SIGKILL escalation — a client disconnect sent only SIGTERM; a
   SIGTERM-resistant child held its concurrency slot until the request timeout
   (narrow #37 on the hottest exit path). Now escalates to SIGKILL 5s after SIGTERM,
   cleared on proc exit. Per review: gated on the child still being alive
   (exitCode===null && signalCode===null) so the normal-success close no longer
   fires a spurious SIGTERM or leaks the 5s timer; killTimer.unref() so a genuine
   disconnect timer never delays graceful shutdown.

3. Per-key quota TOCTOU — documented as best-effort/eventually-consistent (inline
   comment + README note): concurrent requests at the boundary can overshoot by up
   to MAX_CONCURRENT and cache hits are uncounted. Chose documentation over an
   in-flight counter to avoid a decrement-on-all-paths liability (the #37 class) on
   a low-blast-radius internal family rate-limiter — not a payment boundary.

4. [P3] overallTimer cleared on semantic completion — the request timer was cleared
   only on proc exit, so a streamed response that res.end()'d before the child
   exited could record a spurious post-success timeout. New clearOverallTimer()
   (clears the timer ONLY, never touches the `cleaned` slot-accounting flag — no
   slot leak) is called in the streaming stop-success path; cleanup() on exit still
   clears it idempotently and decrements the slot.

ALIGNMENT.md: error-shaping / process-lifecycle / rate-limit documentation forward
no Anthropic operation, so a cli.js citation is N/A under Rule 2. No blacklisted
tokens or port literals introduced; alignment.yml passes.

Independent fresh-context reviewer (opus): APPROVE WITH MINOR (Iron Rule 10) — the
two critical concerns (clearOverallTimer slot-leak class, SIGKILL double-kill) were
verified clean; MINOR #1 (kill-timer leak on success path) folded in; MINOR #2
(pre-existing regex over-redaction of ratios/URLs) left as out-of-scope.

Closes #111.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:34:19 +10:00
36be723198 fix(security): gate /health anonymousKey behind opt-in PROXY_ADVERTISE_ANON_KEY (#109) (#116)
/health is an unauthenticated, LAN-reachable endpoint that returned the live
anonymous bearer key (anonymousKey: PROXY_ANONYMOUS_KEY). Any device that could
reach the port harvested a working, quota-spending credential (P0).

The anonymousKey field is now included only when the caller is localhost (already
fully trusted by the auth path, via the unspoofable req.socket.remoteAddress) OR
the admin explicitly opts in with the new PROXY_ADVERTISE_ANON_KEY=1 env var
(default off). ocp-connect's absent-field fallback (interactive --key / anonymous
access) is unchanged — comment-only update there.

ALIGNMENT.md: this gates an OCP proxy-policy field in /health; it does NOT forward,
add, or alter any Anthropic API operation, so a cli.js citation is N/A under Rule 2.
No blacklisted tokens introduced; alignment.yml passes.

Independent fresh-context reviewer (opus): APPROVE WITH MINOR (Iron Rule 10).

Closes #109.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 22:12:26 +10:00
7b065600aa docs(readme): honest multi-user/security positioning + 6/15 framing + OLP cross-link (#108)
- Deployment model & security section: single-user multi-IDE is the supported
  model; shared/multi keys give usage/quota tracking but NOT a security isolation
  boundary (claude runs with operator FS, not sandboxed) — trusted users only;
  real per-user sandboxed isolation is planned post-2026-06-15.
- Soften the multi-mode 'recommended' label accordingly.
- Add a 'Related: OLP' section linking the multi-provider sibling project.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:44:25 +10:00
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>
2026-05-31 09:21:11 +10:00
885f62addf feat: Phase 6c — claude stream-json port + --system-prompt + opus 4.8 (#103)
* wip(rescue): OCP Phase 6c stream-json port + opus 4.8 (uncommitted /tmp work)

Rescue commit — preserves the subagent-produced Phase 6c port (claude -p →
stream-json + --system-prompt + NDJSON parser) and opus 4.8 model addition
done 2026-05-30 in a /tmp clone (volatile). NOT reviewed-for-merge; this is
preservation only on a WIP branch so a /tmp reboot does not lose the work.

Strategy context: OCP is being made the first-mover for TUI-mode (users are
on OCP). Phase 6c here is the DEFAULT-path foundation (stream-json = sdk-cli =
the "Max-user stable mode" fallback). TUI-mode (cc_entrypoint=cli subscription
bridge) lands on top as an opt-in. Re-review before merging to main.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(phase6c): fold review findings — scrub hostname, README prose, parser tests, version citation

P3-1 (binding): scrub private hostname "PI231" from server.mjs comment; replace
with role-based term "the test server". Repo is public — no machine-specific
hostnames may appear.

P3-2: update README.md "How It Works" diagram and prose from stale "claude -p"
to "claude --output-format stream-json", matching the Phase 6c spawn change already
in server.mjs and CHANGELOG.

P2-2: update all five authority citations in server.mjs from bare "claude CLI v2.1.104"
to "claude CLI § <flag> (ported from OLP, verified v2.1.104; behavior stable through
v2.1.158)" — honest about OLP provenance and version range, without inventing new facts.

P2-1: add "Stream-JSON parsers" suite to test-features.mjs (17 new tests, 84 → 101
total). Copies parseStreamJsonLines and parseStreamJsonEvent verbatim from server.mjs
with a MIRRORS header comment; logEvent stubbed to avoid live-server side-effects.
Covers: content_block_delta deltas, assistant-aggregate fallback, no-double-count guard,
aggregate-only short responses, partial-line buffering across two chunks, is_error result
variants, malformed/non-JSON lines, system/user/unknown event types.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:13:36 +10:00
7766fa0868 fix(plugin): revert default to 3456 + correct v3.16.1 narrative; v3.16.2 (#96)
v3.16.1's narrative ("OCP server moved to 3478 default in v3.14+") was
incorrect. OCP source default has been 3456 since 593d0dc (initial
release) and never changed. The single observation of 3478 is the
maintainer's Mac mini, whose plist was rewritten with --port 3478
during a 2026-05-08 PR #71 dogfood smoke-test accident (see
~/.cc-rules/memory/learnings/subagent_setup_mjs_prod_host_collision.md).
The drift was never reconciled and v3.16.1 mistakenly canonised the
post-accident port as the new default.

This release reverts:
- ocp-plugin/index.js fallback → http://127.0.0.1:3456
- openclaw.plugin.json configSchema.proxyUrl.default → http://127.0.0.1:3456
- README §Environment Variables CLAUDE_PROXY_PORT default → 3456
- top-level package.json → 3.16.2

PR #95's env-reading path (OCP_PROXY_URL → CLAUDE_PROXY_PORT → fallback)
is preserved — that part was good design and stays. Only the hardcoded
fallback default changes.

Hosts whose OCP plist injects a non-default port must also inject the
same CLAUDE_PROXY_PORT into the OpenClaw plist for the plugin to follow
(documented in the new index.js comment block).

Mac mini's plist was reverted from 3478 to 3456 as part of this deploy
(per-host correction; no source code reflects host-specific state).

CHANGELOG includes an explicit erratum entry under v3.16.1 marking it
superseded.

Process note: this PR was triggered by maintainer asking "why was the
port changed?" — the answer revealed I (PM) wrote v3.16.1's CHANGELOG
without running `git log -G "3478" -- setup.mjs`. Iron Rule 2
(evidence-first) was violated. Future commits asserting historical
facts must include the grep that confirmed them.

No cli.js citation needed: OCP-internal plugin + docs, no server.mjs
change.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 12:17:13 +10:00
70faeff067 fix(plugin): default OCP plugin port to 3478 + env-overridable (v3.16.1) (#95)
* fix(plugin): default OCP plugin port to 3478 + env-overridable; v3.16.1

ocp-plugin/index.js hard-coded http://127.0.0.1:3456 since the plugin
was created. OCP server moved to 3478 default in v3.14+ as part of
the same wave that renamed the launchd label (dev.ocp.proxy). The
plugin never got the memo. Result: `/ocp usage` from OpenClaw bots
(e.g. the home Telegram bot 大内总管) hit the dead port 3456 and
returned "OCP error: fetch failed".

Fix:
- Default PROXY → http://127.0.0.1:3478
- Read OCP_PROXY_URL env (full URL) first
- Else read CLAUDE_PROXY_PORT env (port only, localhost assumed)
- Else fall back to the 3478 default

openclaw.plugin.json bumped (3.12.0 → 3.16.1) and configSchema
default updated. Plugin version now matches OCP version.

Top-level package.json bumped 3.16.0 → 3.16.1. CHANGELOG entry added.

Diagnostic trail: caught 2026-05-12 when home Telegram bot reported
"OCP error: fetch failed" against `/ocp usage`. Mac mini OCP service
was healthy on port 3478; lsof -iTCP:3456 had no listener; plugin
index.js had hardcoded 3456.

No cli.js citation needed: this is OCP-internal plugin code with no
corresponding cli.js operation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(readme): document OCP_PROXY_URL + CLAUDE_PROXY_PORT plugin reuse (per release_kit 5.5)

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 11:51:54 +10:00
ab03c13332 feat(upgrade): ocp doctor + cross-version ocp update + rollback + AI prompts (#91)
* feat(doctor): add ocp doctor with --json + next_action contract

Implements scripts/doctor.mjs with semver-aware path selection
(noop/update/upgrade/fresh_install/fix_oauth/fix_service) and the JSON
contract documented in the design spec.

Service health + OAuth checks integrated; mockable via opts.mockHealth
for unit tests. 8 unit tests cover the kind dispatch tree and the
next_action shape for each kind.

No cli.js citation needed: this is OCP-internal tooling with no
corresponding cli.js operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ocp): wire cmd_doctor into bash CLI; dispatch to scripts/doctor.mjs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(doctor): handle unparseable version + empty health body

Three issues raised by code-quality reviewer on b65201b:

1. semverCompare returned 0 for unparseable input, causing fromSupported=true
   and kind=noop for an install with unreadable package.json. Now treats
   unparseable currentVersion as fresh_install candidate.
2. mockHealth: { status: 200, body: null } routed to fix_oauth (because
   health.body?.auth?.ok was undefined → falsy). 200 with empty body is
   server-broken, not OAuth-broken; now routes to fix_service.
3. Removed unused KIND_ENUM declaration (dead code).

Two regression tests added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(upgrade): add scripts/upgrade.mjs + scripts/lib/snapshot.mjs

Implements the upgrade dispatcher (noop / dry-run / light delegation /
full path) and the snapshot writer/reader/list module. Full path snapshots
plist + db + admin-key + openclaw.json before mutating, runs the 6 phases
(pre-flight, snapshot, fetch+install, reconfigure, restart, post-flight),
and emits a heads-up before launchctl bootout per
notify_before_prod_service_restart.md policy.

mockExec/mockDoctor injection points let tests verify the phase ordering
without touching the real shell. fresh_install + rollback paths are
deferred to Bundle 3.

No cli.js citation needed: this is OCP-internal upgrade tooling with no
corresponding cli.js operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(upgrade): error path completeness + observability

5 issues raised by code-quality reviewer on c12013a:

A. exec() wrapper now captures stderr from execSync failures and
   re-throws with `phase X failed: <stderr>` instead of the terse
   "Command failed: ..." default. Operators see the actual git/npm
   error.
B. runFullUpgrade body wrapped in try/catch; any error after phase 2
   (snapshot written) carries snapshotPath + phases + hint pointing
   at `ocp update --rollback`. Aligns with the post-flight failure
   pattern.
C. CLI entrypoint now prints snapshotPath + hint on error.

Plus minor:
- snapshot.mjs tryCopy logs a [snapshot] warn line instead of silently
  swallowing copy errors (e.g. permission-denied admin-key)
- heads-up window 1s → 3s, more operable per the policy intent
- opts.yes intent comment added (Bundle 3 will use)

One regression test added.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(upgrade): fresh-install + rollback paths

Implements the two missing branches of runUpgrade dispatcher:

- runFreshInstall: gated by --yes, runs doctor.next_action.ai_executable
  steps in order, fails fast on first error, attaches steps[] to thrown
  errors. Accepts mockExec for unit tests.
- runRollback: locates latest or named snapshot in ~/.ocp/, reads
  from-commit.txt, restores plist + db + admin-key + service file (with
  per-file warn lines on copy failure), git-checkouts the from-commit,
  npm installs at that revision, restarts the service. --list shows all
  snapshots; --dry-run prints the plan without mutation.

Both paths use the same exec() error-wrap pattern as runFullUpgrade
(stderr capture, phases attached to thrown errors, restart heads-up).

CLI entrypoint extended to parse --rollback / --list / --target / and
optional positional snapshot path after --rollback.

6 unit tests cover: --yes gate, fresh_install ai_executable run,
--rollback --list, no-snapshots error, --rollback --dry-run, mock-exec
restore.

No cli.js citation needed: this is OCP-internal upgrade tooling with
no corresponding cli.js operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(upgrade): nit fixes from Bundle 3 code-quality review

3 micro-fixes on 48e9408:
1. Remove unused mkdirSync import
2. snapshot-not-found error message hints "must be inside ~/.ocp/upgrade-snapshot-*"
3. runFreshInstall failure now includes e.stderr (or e.message fallback) in the
   thrown error and steps[].error so non-interactive callers see the actual reason

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(ocp): cmd_update dispatches via doctor; --rollback added; light path preserved

cmd_update now calls scripts/doctor.mjs to determine which path to take:
  noop          → "already at latest" exit 0
  update        → existing light path (git pull + npm install + restart),
                  extracted into _cmd_update_light helper to keep the daily
                  case fast and shell-only
  upgrade       → exec node scripts/upgrade.mjs (full path with snapshot
                  + post-flight)
  fresh_install → exec node scripts/upgrade.mjs (gated by --yes)
  fix_oauth/fix_service → print error referring user to `ocp doctor`

cmd_update --rollback path: exec node scripts/upgrade.mjs --rollback "$@"
forwards remaining args (--list, --dry-run, optional snapshot path).

cmd_update_help expanded to document new flags.

cmd_update --check fast path is preserved exactly (no doctor call there).

No cli.js citation needed: this is OCP-internal CLI dispatch with no
corresponding cli.js operation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(ocp): forward all args to cmd_update so multi-flag invocations work

Bug found via runtime smoke test:
  ./ocp update --rollback --list → "no snapshots" (wrong; should list)

Root cause: dispatch was `cmd_update "\${1:-}"` (only first arg). When
user typed `--rollback --list`, cmd_update only received `--rollback`,
the shift left $@ empty, and exec node ... --rollback got no flags.
Other commands using "\${1:-}" don't need multi-arg, but cmd_update now
does (--rollback --list, --rollback --dry-run, --target X --yes, etc.).

Change: dispatch is now `cmd_update "\$@"`. cmd_update internals already
handle multi-arg correctly (\$1 == --check fast path; \$1 == --rollback
shift+forward; otherwise doctor-driven).

Verified:
  ./ocp update --check         → existing behaviour preserved
  ./ocp update --rollback --list → "Found 0 snapshots:" exit 0
  ./ocp update --rollback --dry-run → no-snapshot error exit 1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(release): v3.15.0 — README AI prompt blocks + Upgrading rewrite + CHANGELOG

§Installation, §Upgrading, §Troubleshooting each start with a copy-paste
AI prompt block for Claude Code / Cursor / Copilot. The Upgrading section
explains the three paths (light / full / fresh-install) and rollback usage.

All Commands table gains an `ocp doctor` row.

package.json bumped to 3.15.0.

CHANGELOG.md gains the v3.15.0 entry covering doctor, the cross-version
update path, --rollback, fresh-install routing, and AI prompt blocks.
Notes the dependency on PR #90 (plist env merge bug fix, already merged).

No cli.js citation needed: docs + version bump only, no server.mjs change.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(readme): show --yes in rollback usage examples

Per Iron Rule 10 reviewer nit on PR #91: live rollback requires --yes
even for interactive humans. Update §Upgrading examples to show the
canonical human form. (AI agents pass --yes by convention; humans were
hitting a confusing "requires --yes" error following the prior README.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(scripts): CLI entrypoint guard resilient to symlinked install paths

Bug found via integration test on MacBook Pro (macOS /tmp → /private/tmp):
`import.meta.url === \`file://\${process.argv[1]}\`` evaluates false when
the install path traverses a symlink, because import.meta.url is canonicalised
but process.argv[1] is not. Result: ./ocp doctor (and ./ocp update via
upgrade.mjs) exit silently with code 0 and no output, instead of running.

Fix: use fileURLToPath + realpathSync on both sides of the comparison.
Affects any install at a symlinked path (/tmp, NFS mounts, /var/ paths,
docker bind mounts, etc.). Normal ~/ocp installs were unaffected.

No cli.js citation needed: this is OCP-internal CLI dispatch with no
corresponding cli.js operation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 06:56:17 +10:00
750b25ba77 chore(release): v3.14.0 — security hardening (sessions namespacing + file modes + /api/usage scope) (#89)
Bump version 3.13.0 → 3.14.0. No functional code change in this PR;
all three security fixes are already merged in main via PRs #86, #87, #88.

This PR covers only metadata + docs:
- package.json: version bump to 3.14.0
- CHANGELOG.md: v3.14.0 entry with Features / Behavior changes / Verification /
  Governance sections
- README.md: /api/usage self-scope note in Auth Modes §, API Endpoints table
  row update, file-mode bullet in Important Notes §

cli.js citation N/A: this release PR modifies no server.mjs / setup.mjs / keys.mjs.
The three underlying security PRs (#86, #87, #88) each carry their own
cli.js-citation-not-applicable disclaimer per PR #75 pattern, as they are
OCP-internal access-control, session-state, and file-permission changes with
no corresponding cli.js operation.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 06:49:00 +10:00
a71c939bf8 docs(readme): remove zhihu blog backlink + badge (article was removed) (#85)
The Chinese blog post linked from these two places was taken down by
the 知乎 platform shortly after publication. Both pointers now resolve
to a "content removed" warning page, which is a worse signal to
visitors than no link at all. Removing both before they reach more
README readers.

Reverts the additions from #81 specifically:
- Top badges row: drop the "blog · engineering story" shield
- §Why OCP? closing blockquote: drop the line pointing at the article

The 知乎 article URL is no longer reachable, so retaining the references
would route incoming traffic to a dead page that suggests the project
is itself problematic. Better to leave that section silent until a
working canonical write-up exists somewhere.

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>
2026-05-09 22:43:59 +10:00
fbbf3b6c7c docs(readme): add engineering-story backlink to 知乎 article + badge (#81)
Adds two pointers from README to a Chinese-language engineering blog
post (https://zhuanlan.zhihu.com/p/2036388634207770402) that walks
through OCP's cli.js-alignment philosophy, the 2026-04-11 drift
incident (the 9-day hallucinated /api/oauth/usage endpoint), the
three-tier guardrail design, and the recent fresh-state E2E pass
that produced PRs #74-#78.

Two minimal touches to README only:

1. Badges row: a "blog · engineering story" shield linking to the
   article. Slot fits between the existing Release badge and the
   Buy Me a Coffee badge so the row's visual rhythm is preserved.

2. New blockquote line at the end of §Why OCP? (between the
   single-maintainer / pre-1.0 disclosure and §Supported Tools).
   Brief, factual, no marketing voice.

Why this PR

The 知乎 piece is a self-contained engineering narrative about
maintaining a single-maintainer LLM-assisted proxy without endpoint
drift. README is the project's primary surface; pointing at the
narrative lets readers who land on the repo decide if the project's
discipline matters to them before they invest in install. Reverse
direction: GitHub readers who follow the link bring some traffic
back to the article, validating that engineering content has a
home.

Doc-only change. server.mjs untouched. ALIGNMENT.md Rule 5 (cli.js
citation) does not apply. Same pattern as #68 / #69 / #71 / #78 / #79.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 12:32:52 +10:00
d760d7fcce docs(readme): add restrained star + issue CTA below tagline (#79)
* docs(install): remove false symlink claim, fix Anonymous mode startup command

Three doc inaccuracies surfaced during fresh-state install testing on Pi231 +
MacBook (Round 1+2):

1. README §Server Setup claimed setup.mjs "Symlink `ocp` to /usr/local/bin for
   CLI access" — false. setup.mjs writes start.sh + plist/systemd unit but
   creates no PATH symlink. Removed the line; added a short PATH tip showing
   the user's options (manual symlink to ~/.local/bin or /usr/local/bin, or
   shell alias) right after the install summary.

2. README §Anonymous Access told users to run `ocp start` to enable the
   feature — there is no `ocp start` subcommand. Available commands are
   restart / stop / status / logs / keys / usage / update / lan / health /
   clear / settings (verified via `~/ocp/ocp` enumeration). Replaced with
   the correct flow: export PROXY_ANONYMOUS_KEY, then `node setup.mjs
   --bind 0.0.0.0 --auth-mode multi`.

3. The same paragraph implied that exporting PROXY_ANONYMOUS_KEY in an
   interactive shell is enough to enable anonymous access — but the running
   proxy is auto-started by launchd/systemd from the service unit's own
   env, not from the user's shell. Spelled this out and noted that if OCP
   was installed before exporting the env var, the user must re-run
   setup.mjs (idempotent) so the service unit env is refreshed, then
   `ocp restart`.

The PROXY_ANONYMOUS_KEY mechanism described becomes 100% accurate when
PR B (`fix/setup-inject-service-env`, sibling PR) lands; current setup.mjs
on main does not yet inject this env into the service unit.

Doc-only — no `server.mjs` change, no version bump, no `cli.js` citation
required.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(readme): add restrained star + issue CTA below tagline

A single italic line under the existing personal-note italic, asking
readers to  the repo if they get value, and to file issues — framed
explicitly as "issues are even more useful than stars" so the CTA reads
as feedback-seeking rather than vanity-metric chasing.

Tone matches the rest of README: low-key, single-maintainer self-deprecating,
no marketing voice. No "save money / free / $0" verbiage. Coexists with the
existing buy-me-a-coffee personal-note line above it (different ask:
funding vs. social-proof).

This is doc-only. ALIGNMENT.md Rule 5 (cli.js citation) does not apply.
Same pattern as #68 / #69 / #71 / #78.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 03:01:06 +10:00
c0f2d3ab20 docs(install): remove false symlink claim, fix Anonymous mode startup command (#78)
Three doc inaccuracies surfaced during fresh-state install testing on Pi231 +
MacBook (Round 1+2):

1. README §Server Setup claimed setup.mjs "Symlink `ocp` to /usr/local/bin for
   CLI access" — false. setup.mjs writes start.sh + plist/systemd unit but
   creates no PATH symlink. Removed the line; added a short PATH tip showing
   the user's options (manual symlink to ~/.local/bin or /usr/local/bin, or
   shell alias) right after the install summary.

2. README §Anonymous Access told users to run `ocp start` to enable the
   feature — there is no `ocp start` subcommand. Available commands are
   restart / stop / status / logs / keys / usage / update / lan / health /
   clear / settings (verified via `~/ocp/ocp` enumeration). Replaced with
   the correct flow: export PROXY_ANONYMOUS_KEY, then `node setup.mjs
   --bind 0.0.0.0 --auth-mode multi`.

3. The same paragraph implied that exporting PROXY_ANONYMOUS_KEY in an
   interactive shell is enough to enable anonymous access — but the running
   proxy is auto-started by launchd/systemd from the service unit's own
   env, not from the user's shell. Spelled this out and noted that if OCP
   was installed before exporting the env var, the user must re-run
   setup.mjs (idempotent) so the service unit env is refreshed, then
   `ocp restart`.

The PROXY_ANONYMOUS_KEY mechanism described becomes 100% accurate when
PR B (`fix/setup-inject-service-env`, sibling PR) lands; current setup.mjs
on main does not yet inject this env into the service unit.

Doc-only — no `server.mjs` change, no version bump, no `cli.js` citation
required.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 00:49:49 +10:00
36fa81d1e6 docs(install): add §Quick install with AI assistance + plug five new-user pitfalls (#71)
* docs(install): add §Quick install with AI assistance + plug five new-user pitfalls

Context: a returning user observed that letting an AI assistant follow the
README to install OCP would mostly work but stumble on a handful of small
gaps — missing OS qualifier, no admin-key generation hint, server IP
discovery buried, and a few common setup errors not in Troubleshooting.
This patch closes those gaps and adds a copy-paste prompt section for
new users who'd rather have an AI walk them through the install.

Five additive changes (README only, server.mjs untouched):

1. **§Server Setup prerequisites** — add the macOS/Linux qualifier
   ("Windows is not supported — setup.mjs installs launchd / systemd")
   and `git`, both of which were implicit before.

2. **OCP_ADMIN_KEY generation hint** — replace the placeholder
   `your-secret-admin-key` with a one-line `openssl rand -base64 32`
   example, plus a reminder to add the export to ~/.zshrc / ~/.bashrc
   so it survives shells.

3. **§Client Setup** — add an inline note pointing readers to run
   `ocp lan` on the server to discover the server's LAN IP. Previously
   `ocp lan` was mentioned only in §Server Setup, leaving client-side
   readers to guess.

4. **§Troubleshooting** — three new entries for setup-time errors
   (claude not found / EADDRINUSE 3456 / node version), with the
   specific recovery commands. Existing entries left unchanged.

5. **§Installation → new ###Quick install with AI assistance subsection**
   — three copy-paste prompts (single-machine, LAN server, client) that
   pin the AI to the right README path, name the verification step, and
   forbid silent retries. Includes a pointer to the manual handbook
   sections for readers who prefer that path.

Doc-only change. server.mjs not modified. ALIGNMENT.md Rule 5 (cli.js
citation requirement) does not apply. Same pattern as #68, #69, #70.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(install): add Claude CLI install command + ####Headless install notes

Live Pi231 (RPi4 / Debian Bookworm) test of PR #71's "Quick install with AI
assistance → LAN server" prompt surfaced two more new-user pitfalls in the
README's Prerequisites + Server Setup flow:

1. **Claude CLI install command was missing.** Previous text said "Claude CLI
   installed and authenticated" with a docs link — but the actual install
   command (`npm install -g @anthropic-ai/claude-code`) appeared nowhere.
   An AI assistant following the prompt has to fetch external docs to
   guess at the install path. Now inlined.

2. **Headless servers (Pi / NAS / VPS) had no auth guidance.** OCP's main
   deployment targets are always-on headless devices. `claude auth login`
   actually works headless (prints URL + code, OAuth completes on any
   browser-capable device), and `claude setup-token` provides a long-lived
   token — but neither was documented. New ####Headless install notes
   subsection explains both paths.

Test trail (Pi231):
-  Linux (aarch64 Debian Bookworm), Node v22.22.2, git 2.39.5 — prereqs
  satisfied except Claude CLI.
-  `claude` not on PATH (expected — fresh Pi). README didn't tell the
  user how to install it. → fix #1 above.
- ⚠️ Even after AI fetches the install command, headless OAuth was a
  documentation gap. → fix #2 above.

Doc-only change. server.mjs not modified. ALIGNMENT.md Rule 5 does not
apply. Extends PR #71 (same install-UX layer per Iron Rule 11 IDR).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 15:02:40 +10:00
cce0110253 docs(why-ocp): reorder bullets + soften alignment framing + visceral hooks (#70)
The "Why OCP?" section previously led with technical/governance bullets
(SSE heartbeat, alignment, models.json) and buried the most relatable
selling points (LAN multi-user keys, ocp-connect IDE auto-config, cache).
First-time readers stopped reading before reaching the bullets that would
have sold them on the project.

Changes (README.md only, line 19-26):

1. Reorder: human-relatable benefits first, governance discipline last.
   New order: LAN multi-user → ocp-connect → cache → quota → SSE heartbeat
   → alignment → models.json. Quota is split out from the old combined
   "quota + cache" bullet so each gets its own one-liner.

2. Soften the alignment bullet's framing. The previous prose flagged
   "Other Claude proxies have shipped exactly that" and was deleted —
   no need to call out competitors. Replaced with a measured "LLM-assisted
   code drifts easily — it's tempting to invent plausible-looking endpoints
   that cli.js doesn't actually use" plus a deliberately understated
   payoff: "your setup keeps working when cli.js ships its next minor."

3. Add visceral hooks where bullets benefit from them:
   - SSE heartbeat: "If you've ever watched your IDE die at the 60s idle
     mark during a long Claude tool-use pause — that's nginx/Cloudflare
     default behavior" (frames the problem in user-felt terms before
     describing the fix).
   - Per-key quota: concrete example "set a kid's iPad to 20/day, a
     partner's laptop to 100/week" (replaces abstract "limits per key").

4. Cache bullet now explicitly states the per-key isolation guarantee:
   "cross-user pollution is impossible by hash construction, not by
   application logic" — this addresses the most common pre-adoption
   concern (and reflects the v3.13.0 D1 design).

5. Total length compressed ~20% despite added context — old bullets had
   redundant "what" descriptions; new bullets lead with the "what for".

Doc-only change. server.mjs not touched. ALIGNMENT.md Rule 5 (cli.js
citation requirement) does not apply. Same pattern as #68, #69.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:57:06 +10:00
40391791a1 docs(funding): raise sponsorship visibility — top badges + intro CTA + expanded support section (#69)
Context: Buy Me a Coffee + Stripe onboarding now fully live (verified
buymeacoffee.com/dtzp555 returns og:type=profile with Support CTA, default
$3 price tier, membership tier active). Previous §Support OCP at line 709
was effectively buried — last section before License, missed by most readers.

Changes (README.md only, server.mjs untouched):

1. Top of file — three shields.io badges (License MIT, latest release,
   Buy Me a Coffee) under H1, before tagline. Standard OSS pattern
   (Vue / Vite / Tailwind), high visibility, doesn't disrupt prose.

2. Just under tagline — one-line italic personal note pointing to
   §Support OCP, with inline  link as fallback for users who don't
   scroll. Quotes the spirit of the longer section without duplicating it.

3. §Support OCP expanded — adds the "open source from day one" framing
   (not freemium, not commercial-turned-open), the "my family uses it
   daily" angle, and an explicit feedback / issues invitation. The
   debugging-history paragraph is preserved verbatim.

Doc-only change. ALIGNMENT.md Rule 5 (cli.js citation) does not apply —
server.mjs is not modified. Same pattern as #68.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 13:29:58 +10:00
342a0a44f5 docs(funding): add Buy Me a Coffee link + GitHub FUNDING.yml (#68)
- .github/FUNDING.yml — enables GitHub's native "Sponsor" button on the
  repo page, pointing to buymeacoffee.com/dtzp555. Other platforms
  (GitHub Sponsors, Ko-fi) are commented out and can be enabled later
  by uncommenting + filling in handles.
- README.md § Support OCP — new section just before License. States the
  free-and-open-source commitment, lists the kinds of work that don't
  show up in commits (multi-machine debugging, IDE validation, drift
  incidents, concurrency leaks), and offers a single  link for users
  who want to support continued maintenance. Explicitly disclaims paid
  tiers / premium features so the open-source posture stays unambiguous.

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: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 03:29:27 +10:00
9494fd6c69 chore(release): v3.13.0 — cache layer hardening (per-key isolation + bypass + chunked replay + singleflight) (#67)
Per release_kit overlay (CLAUDE.md § Iron Rule 5.5):
- package.json bumped 3.12.0 → 3.13.0
- CHANGELOG.md updated with v3.13.0 entry
- README.md § Response Cache updated to document the four hardening features

This is a release preparation commit. Tag push to v3.13.0 will trigger
.github/workflows/release.yml which auto-creates the GitHub Release.

cli.js does not perform proxy-layer response caching, stampede protection,
or replay; this release only ships internal cache-layer correctness and
concurrency improvements that do not change the OpenAI-compatible wire
surface visible to clients. No client-observable wire shape change.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 17:19:44 +10:00
3d52ffc152 chore(deps): bump engines to >=22.5 to match node:sqlite usage in keys.mjs (#62)
OCP uses Node.js built-in SQLite (`node:sqlite`) in keys.mjs:3 for the
LAN-mode key store. The `node:sqlite` module is only available in:

- Node 22.5.0+ behind --experimental-sqlite flag
- Node 23.0.0+ without any flag (fully stable)

The previous `engines: ">=18"` was inaccurate and would have caused
opaque "Cannot find module 'node:sqlite'" failures for users on
Node 18-22.4 the moment LAN mode (multi-key) was enabled.

Changes:
- package.json: engines.node ">=18" → ">=22.5"
- README.md: prerequisite "Node.js 18+" → "Node.js 22.5+ (Node 23+ recommended …)"
  with a one-line note explaining the flag distinction so users on 22.x know
  they may need --experimental-sqlite.

Verification:
- Source usage of node:sqlite confirmed via grep: keys.mjs:3
  (`import { DatabaseSync } from "node:sqlite";`).
- Local node --version = v25.8.0 (well above 22.5); `npm install` exit 0
  with no engine-mismatch warning.
- No other source file imports node:sqlite (single point of usage).

Refs: audit side-finding 2 of 4.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 09:53:46 +10:00
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>
2026-05-05 09:44:49 +10:00
e4b010af5e docs(readme): add Why OCP, comparison table, governance section (#53)
Adds three positioning sections to make the README convert clones-to-stars
better:

1. "Why OCP?" near the top — 6 differentiator bullets with evidence links
   (SSE heartbeat / ALIGNMENT.md / models.json SPOT / multi-key /
   per-key quota / ocp-connect).
2. "Comparison" subsection — honest table vs claude-code-router and
   anthropic-proxy. Acknowledges CCR's larger ecosystem; positions OCP
   as cli.js-aligned + subscription-multiplexing focused.
3. "Governance" section near the bottom — links to ALIGNMENT.md, AGENTS.md,
   ADRs, alignment.yml. Consolidates the governance-link surface in one
   place rather than scattered.

Net diff +43 -0. No content removed. No anchors broken. No code changes.

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 09:44:37 +10:00
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>
2026-04-25 09:09:47 +10:00
cff06439fa chore(privacy): remediation follow-up from ocp#44 (#45)
Three-part follow-up from the 2026-04-22 privacy postmortem:

1. OAUTH_CLIENT_ID verified as public Claude Code constant (not a secret).
   Added gitleaks allowlist entry. The value 9d1c250a-e61b-44d9-88ed-5944d1962f5e
   is the public PKCE client ID used by the Claude Code CLI — public clients in
   PKCE flows have no client secret, so the ID itself carries no secret value.
   Introduced in commit b87992f (the 2026-04-11 drift incident); the constant
   mirrors what cli.js embeds for its OAuth token-refresh flow.

2. README.md API key example made clearly fake (ocp_example12345abcde...) to
   avoid gitleaks false-positives and clarify to readers it is not real.
   The ocp_ prefix IS the real prefix (keys.mjs line 80: randomBytes(24).base64url),
   so the prior example could be mistaken for a real truncated key.

3. PR template: added Privacy self-check section for PUBLIC repos below the
   existing 5.3 user-visible change self-check section.

4. .gitleaks.toml: new file with allowlist for the three confirmed non-issues
   (OAUTH_CLIENT_ID constant, README placeholder regex, old plan doc path).

No code or behavior change beyond the docs, README, and new config file.

Closes part of ocp#44.

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 11:40:50 +10:00
2e634f708b docs(readme): document v3.11.0 features (auto-sync, models.json SPOT, troubleshooting) (#34)
Backfills user-visible feature documentation for v3.11.0 that was missing
from PR #33 (which only fixed stale references). Per Tao's review: shipping
a feature without README documentation is a worse failure than stale
references, since users don't know the capability exists.

Added:
- "Self-Update" section: explain the full ocp update pipeline order
- New "OpenClaw Auto-Sync (v3.11.0+)" section: when it triggers, what
  gets synced (with strict scope boundaries), safety guarantees, manual
  invocation, opt-out, bootstrap caveat, behavior for non-OpenClaw IDEs
- "Available Models" expanded: explain models.json as SPOT, contributor
  workflow for adding a new model
- "Troubleshooting" two new entries:
    - "Startup log warns OpenClaw registry out of sync"
    - "OpenClaw shows old models after ocp update (v3.10→v3.11 only)"

No code changes. README only.

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:27:51 +10:00
820abc4b89 docs(readme): refresh model list and version references for v3.11.0 (#33)
- Verify-curl example: 3 stale ids → 4 current ids
- Connect-script output sample: v3.10.0 → v3.11.0, "3 models" → "4 models"
- OpenClaw setup output: add ocp/claude-opus-4-7 to the example
- Available Models table: add opus-4-7, retain opus-4-6 for pinning, mark default aliases, link to models.json as canonical source
- OpenClaw Integration section: add bullet for ocp update auto-sync (v3.11.0+)

No code changes. README only.

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 20:19:51 +10:00
43cd7712e6 chore(release): v3.10.0 — Claude Opus 4.7 support (#28)
Minor bump. User-visible new feature: Claude Opus 4.7 model is now
selectable via OCP.

- New model id 'claude-opus-4-7' in MODEL_MAP and /v1/models
- Short aliases 'opus' and 'claude-opus-4' now route to 4.7
- 'claude-opus-4-6' remains explicit for pinned usage

Evidence chain (see PR #27):
  - Anthropic /v1/models: claude-opus-4-7 (display 'Claude Opus 4.7')
  - claude.exe v2.1.114 strings: claude-opus-4-7 present
  - Live probe verified 2026-04-20

No wire-format breaking changes.

Co-authored-by: Oracle Public Cloud User <opc@instance-20230820-1333.subnet07301351.vcn07301351.oraclevcn.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 15:52:30 +10:00
7cff33cc18 chore(release): v3.9.0 — alignment constitution + usage endpoint restoration (#26)
Minor version bump covering substantive functional + governance changes
since v3.8.0:

Functional
  - Restore header-based /usage probe (reads anthropic-ratelimit-unified-*
    response headers; mirrors Claude Code cli.js vE4). Progress bars
    work again after 9-day drift. PR #21 (fd7973a) + #24 (01e260c).
  - Remove stale-cache compensation for the hallucinated endpoint. PR #23.
  - CLAUDE_CODE_OAUTH_TOKEN environment variable fallback for credentials.
  - Exponential backoff on OAuth refresh (60s -> 3600s) to prevent tight
    retry loops that previously burned rate-limit in seconds.

Governance
  - ALIGNMENT.md project constitution (five binding rules, annual audit
    pinned to 11 April, Historical Lesson recording b87992f drift).
  - CLAUDE.md session instructions requiring cli.js citations for every
    server.mjs change.
  - .github/PULL_REQUEST_TEMPLATE.md with mandatory alignment evidence.
  - .github/workflows/alignment.yml CI guardrail (hard-fail blacklist +
    soft-check commit citation).

No API / wire-format breaking changes.

Co-authored-by: Oracle Public Cloud User <opc@instance-20230820-1333.subnet07301351.vcn07301351.oraclevcn.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 15:24:36 +10:00
b908fec7b4 docs(readme): sync to v3.8.0 (#19)
- Remove "Status: Stable — Feature-complete, bug fixes only" tagline
- Add Per-Key Quota section with curl examples and 429 response format
- Add Response Cache section with enable/management instructions
- Update API Endpoints table with new quota + cache endpoints
- Add CLAUDE_CACHE_TTL to Environment Variables table
- Update version references from v3.7.0 to v3.8.0

Co-authored-by: Tao Deng <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 07:04:08 +10:00
f3745fa8fe docs(readme): sync to v3.7.0 — anon key, auto-discovery, current model IDs (#17)
Brings README up to date with the four PRs merged on 2026-04-12:

* #13 ocp-connect v1.1.0 — per-agent auth-profiles write
* #14 ocp-connect v1.2.0 — IDE detection rewrite + hint density
* #15 OCP server v3.7.0 — PROXY_ANONYMOUS_KEY allowlist
* #16 ocp-connect v1.3.0 — auto-discover anonymous key from /health

## Changes

1. Banner updated `v3.6.0` to `v3.7.0`.

2. New "Zero-config" paragraph in Client Setup showing
   `./ocp-connect <server-ip>` works without `--key` when the
   server admin has set PROXY_ANONYMOUS_KEY.

3. Client Setup example output replaced to reflect v1.3.0 actual
   behavior: `OCP Connect v1.3.0` banner, `Remote OCP v3.7.0`,
   new `ⓘ Using server-advertised anonymous key` block, new
   `Per-agent auth profile seeded (2)` line with both main and
   macbook_bot agentDir paths, new three-line smoke test caveat,
   and correct `claude-haiku-4-5-20251001` model ID.

4. "The script automatically" list expanded from 3 bullets to 5:
   added auto-discovery note (v1.3.0+/v3.7.0+), added OpenClaw
   per-agent profile note, corrected IDE hint bullet to list
   Cline/Continue.dev/Cursor/opencode explicitly as "manual
   configuration required" rather than the earlier inaccurate
   "configures" wording.

5. Available Models table updated `claude-haiku-4` to
   `claude-haiku-4-5-20251001` to match the actual `/v1/models`
   response.

6. Environment Variables table: new `PROXY_ANONYMOUS_KEY` row
   added after `PROXY_API_KEY`, with a link to the existing
   "Anonymous Access (optional)" section.

## Not changed

* `package.json` already at 3.7.0 from #15.
* ocp-connect script unchanged (v1.3.0 from #16).
* server.mjs unchanged (v3.7.0 from #15).
* No behavioral changes. README text only.

## Test

`git diff --stat main -- README.md` = +24 -7 on one file. Markdown
spot-checked for broken table alignment and link targets. The
anonymous key section anchor `#anonymous-access-optional` resolves
to the existing heading at line 234.

Co-authored-by: taodeng <taodeng@Taos-MBP>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 21:29:06 +10:00
38070fbabb feat(server): anonymous key allowlist for multi-mode (v3.7.0) (#15)
Implements issue #12 section 14 Path A. Lets OCP admin designate a
single well-known "anonymous" key that bypasses validateKey() while
keeping AUTH_MODE=multi. Gives OpenClaw multi-agent clients (which
MUST send a non-empty Authorization header per their per-agent
auth-profiles schema) a way to connect without every user needing
a personal key.

## Background

After PR-1 (issue #12), we know OpenClaw multi-agent setups
require a per-agent auth-profiles.json with a non-empty `key`
field. OCP multi-mode rejects any non-empty Bearer token that
isn't in the keys database (server.mjs line 1152), which creates
a deadlock: the only way for OpenClaw to use OCP is with a real
admin-issued key.

Path A resolves this by letting the admin opt in to a public
anonymous key that clients can auto-discover via /health. OpenClaw
writes this key into its agent profiles just like a real key; OCP
server short-circuits validateKey when the key matches the
allowlist. No per-user coordination needed, admin still controls
the policy (can rotate, can unset, can rate-limit).

## Changes

### server.mjs

* Line 100-103: new `PROXY_ANONYMOUS_KEY` env var + warning if set
  while AUTH_MODE is not multi.
* Line 1127-1138 (localhost branch): anonymous allowlist short-circuit
  before validateKey so localhost clients using the anon key are
  labeled "anonymous" instead of "local" in stats.
* Line 1153-1163 (multi branch): the same allowlist check between
  the ADMIN_KEY check and validateKey. Uses timingSafeEqual with
  explicit length check (consistent with the admin and shared key
  patterns above).
* Line 1221 (/health response): new `anonymousKey` field, null when
  not set, the actual value when set. Admins opted into public
  access when they set the env var, so exposing the key here is
  intentional and lets ocp-connect auto-discover it without
  out-of-band coordination.

### package.json

Version 3.6.0 to 3.7.0 (per dev iron rule 5, version bump before push).

### README.md

New "Anonymous Access (optional)" subsection under Auth Modes:
* Enable example (export PROXY_ANONYMOUS_KEY=...)
* Client-side /health discovery explanation
* Security note: opt-in to public access, rate-limit warning
* "Not a secret" note: /health is unauthenticated, the key is
  publicly readable by design; treat it as a convenience handle,
  not as an access credential.

## Test evidence

Offline tests on macOS 13, local server on 0.0.0.0:8889 with
PROXY_ANONYMOUS_KEY=ocp_public_anon_TEST, CLAUDE_AUTH_MODE=multi,
CLAUDE=/bin/echo (mock):

* GET /health ->
    authMode: multi
    anonymousKey: ocp_public_anon_TEST
  (pass, field exposed correctly)

* POST /v1/chat/completions with Bearer ocp_WRONG_KEY from
  172.16.2.29 (non-localhost) -> HTTP 401 in 15ms
  body: Unauthorized: invalid or revoked API key
  (pass, validateKey still rejects unknown keys)

* POST /v1/chat/completions with Bearer ocp_public_anon_TEST from
  172.16.2.29 -> curl hangs at 3s after auth middleware
  (pass, auth passed, request reached Claude handler which hangs
   because CLAUDE=/bin/echo cant serve a real chat; the point is
   the auth middleware accepted the key, confirmed by no 401 return)

* POST /v1/chat/completions with no Authorization from 172.16.2.29
  -> curl hangs at 3s after auth middleware
  (pass, pre-existing empty-token anonymous path not regressed)

node --check server.mjs: syntax OK.

## Code review

One independent opus reviewer. Verdict: PASS WITH CONCERNS, zero
blockers. Two strong-recommend items addressed in this commit:

1. Localhost branch was not covered in the first draft. Reviewer
   pointed out that a localhost client using the anon key would be
   labeled "local" instead of "anonymous" in stats, making operations
   visibility worse. Fixed by applying the same anonymous allowlist
   check in the localhost branch at line 1127-1138.

2. README security note was missing the explicit "not a secret"
   framing. Added a paragraph clarifying that because /health is
   unauthenticated, the anonymous key is publicly readable by
   anyone who can reach the server, which is intentional.

Reviewer nits (tokenBuf3 naming, stats subcategory for header-less
vs anon-key anonymous) are deferred as they do not affect
correctness.

## Upstream dependencies on this commit

After this PR is merged and the OCP instance at 172.16.2.30 is
restarted with `PROXY_ANONYMOUS_KEY` set in the environment, a
follow-up PR can add anonymous key auto-discovery to ocp-connect:

* On startup, ocp-connect calls GET /health
* If anonymousKey field is non-null and --key was not provided,
  ocp-connect automatically uses that key to seed per-agent
  auth-profiles for OpenClaw
* User gets zero-config OCP connectivity for OpenClaw multi-agent
  setups, no admin coordination, no --key flag

That follow-up is NOT in this PR. This PR is server-only.

Co-authored-by: taodeng <taodeng@Taos-MBP>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 19:36:26 +10:00
taodengandClaude Opus 4.6 e326cee9dd docs: remove outdated troubleshooting workarounds
Removed v3.0.x migration guide, old idleTimeoutSeconds workaround
(auto-handled by setup.mjs since v3.2.1), and manual session cleanup
instructions. Replaced with concise troubleshooting for common issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 13:29:05 +10:00
taodengandClaude Opus 4.6 f8ca9b85b0 bump version to 3.6.0 for IDE auto-configuration feature
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 13:27:47 +10:00
taodengandClaude Opus 4.6 c22b0dd074 feat: interactive IDE configuration in ocp-connect
ocp-connect now detects installed IDEs and offers to configure them
automatically. OpenClaw gets full interactive setup (provider name,
primary/backup priority, model aliases). Other IDEs (Cline, Continue.dev,
Cursor) get manual config instructions printed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 13:27:07 +10:00
taodengandClaude Opus 4.6 7f46405152 fix: ocp-connect writes to all shell rc files + system-level env vars
- Writes to both .bashrc and .zshrc on macOS (covers both shells)
- macOS: launchctl setenv for GUI apps and daemons
- Linux: ~/.config/environment.d/ocp.conf for systemd user services
- Ensures IDEs and daemons can discover OCP models

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 12:58:27 +10:00
taodengandClaude Opus 4.6 02c6758a61 feat: CLAUDE_NO_CONTEXT mode to suppress context injection (closes #11)
Adds CLAUDE_NO_CONTEXT=true env var that passes
CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 and CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
to Claude CLI child processes. This suppresses CLAUDE.md and auto-memory
injection while preserving OAuth auth — needed for third-party agents
like Hermes that have their own memory systems.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 16:40:36 +10:00
taodengandClaude Opus 4.6 6d0e43ec37 fix: ocp-connect tries anonymous access before requiring key
In zero-config auth mode, the server allows anonymous access even in
multi mode. The script now probes /v1/models first — if it succeeds,
no key is needed. Also updated README examples and version to 3.5.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 13:42:00 +10:00
taodengandClaude Opus 4.6 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>
2026-04-11 09:48:33 +10:00
taodengandClaude Opus 4.6 ed53c5e0c5 docs: add Telegram/Discord /ocp slash command usage to README
Clarify that /ocp commands are for Telegram/Discord via gateway plugin,
while terminal CLI uses ocp (without slash).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 21:48:55 +10:00
taodengandClaude Opus 4.6 cc745aa5d9 docs: update README with complete LAN mode reference
Add LAN CLI commands (keys, lan, usage --by-key), new API endpoints
(dashboard, keys, usage), LAN env vars (CLAUDE_BIND, CLAUDE_AUTH_MODE,
OCP_ADMIN_KEY), and expanded security section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 21:45:07 +10:00
taodeng 43daf8162c docs: add LAN mode documentation and family sharing guide 2026-04-10 21:17:25 +10:00
taodengandClaude Opus 4.6 8796a5fc19 docs: update README version to v3.3.1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 07:49:54 +10:00
taodengandClaude Opus 4.6 3843ec8fe6 refactor: simplify timeout to single CLAUDE_TIMEOUT (default 600s)
Remove firstByteTimeout, adaptive tier algorithm, and MODEL_TIMEOUT_TIERS.
Claude tool-use causes 30s-5min pauses in token streams, making fine-grained
timeouts unreliable — they repeatedly killed valid requests. A single generous
timeout (10 min, matching LiteLLM/OpenAI SDK defaults) is simpler and correct.

Breaking: CLAUDE_FIRST_BYTE_TIMEOUT env var removed, default timeout changed
from 300s to 600s. Bump to v3.3.0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 09:46:12 +10:00
taodengandClaude Opus 4.6 a6007393a3 fix: auto-configure idleTimeoutSeconds=0 to prevent tool-call timeouts
setup.mjs now sets agents.defaults.llm.idleTimeoutSeconds=0 in
openclaw.json during installation. Without this, OpenClaw's default
60s idle timeout kills Claude connections during tool use (Bash, Read,
etc.), causing exit 143 errors and stuck sessions.

Also adds troubleshooting section to README. Bump to v3.2.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 08:50:19 +10:00
taodengandClaude Opus 4.6 8592150f7a docs: update README for v3.2.0 — add update command, upgrade notes
- Document ocp update / ocp update --check
- Add Self-Update section
- Add Upgrading from v3.0.x migration notes
- Remove Known Issues (resolved upstream)
- Note neutral service names in OpenClaw Integration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-03 08:22:55 +10:00
taodengandClaude Opus 4.6 2b05558b8b rebrand: Open Claude Proxy — multi-IDE positioning
Renamed from OpenClaw Control Plane to Open Claude Proxy. README
rewritten to position OCP as a universal proxy for any IDE that
speaks the OpenAI protocol, not just OpenClaw.

Added: supported tools table (Cline, OpenCode, Aider, Continue.dev),
simplified Quick Start, architecture diagram showing multi-IDE flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 12:06:41 +10:00
taodengandClaude Opus 4.6 d9b7c0d905 docs: add Known Issues — /ocp Unknown skill workaround
Documented the intermittent "Unknown skill: ocp" issue caused by
OpenClaw gateway session routing bug (#26895). Workaround: /new
followed by /ocp restart.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 20:50:17 +10:00
taodengandClaude Opus 4.6 9dd35c90ae docs: remove premature Known Issues section
The slash session workaround was incomplete — root cause still under
investigation. Removed to avoid misleading users. Will add back with
accurate info once the issue is fully resolved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 11:04:38 +10:00
taodengandClaude Opus 4.6 7390a19ec6 docs: add prominent warning about slash session bug near plugin install
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:37:10 +10:00