mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
* 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 onb65201b: 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 onc12013a: 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 on48e9408: 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>
9.4 KiB
9.4 KiB
Changelog
v3.15.0 — 2026-XX-XX (release date filled at tag time)
Features
ocp doctor— health & upgrade-readiness check; primary entry for AI-driven debugging.--jsonmode emits anext_actionwithai_executable[]for agents to run verbatim andhuman_required[]for steps requiring the user (typically only OAuth).ocp updatecross-version path — for cross-minor jumps (e.g. v3.10 → v3.14),ocp updatenow runs doctor → snapshot →setup.mjs(with the plist env-merge from PR #90) → service restart → post-flight/health+/v1/modelsverification. Same-patch updates retain the existing light path; users see no change for routine patch bumps.ocp update --rollback— restore the most recent (or specified) upgrade snapshot. Snapshots are saved to~/.ocp/upgrade-snapshot-<ISO-ts>/and never auto-deleted.- Fresh-install routing —
ocp updateon installations < v3.4.0 routes to a fresh-install flow (with--yesto skip confirmation; AI agents pass this). OAuth survives via Claude Code's credential store; users do not re-OAuth unless their token was independently broken. - AI prompt blocks in README — §Installation, §Upgrading, and §Troubleshooting each start with a copy-paste prompt for Claude Code / Cursor / Copilot, so users can drive install / setup / upgrade through their existing AI assistant.
Behavior changes
ocp updatemay take 10–30s longer when a cross-minor jump triggers the full path (snapshot + post-flight). Patch bumps are unchanged.- Pre-v3.4.0 installs are routed to fresh-install rather than failing silently or half-migrating.
Governance
- No
cli.jscitation needed (noserver.mjschange). ALIGNMENT.md Rule 2 not engaged. - Depends on PR #90 (plist env merge bug fix; merged before this release).
v3.14.0 — 2026-05-10
Features (security hardening)
- Per-key session isolation (PR #86, S1) — the
sessionsMap inserver.mjsis now keyed by${keyName}|${conversationId}instead of bareconversationId. Before this fix, two clients using distinct API keys but the samesession_idvalue (e.g. both defaulting to"default") would share the samecli.jssubprocess and conversation history, creating a cross-tenant leak path. Post-fix each (key, session) pair is isolated end-to-end, extending the per-key cache isolation shipped in v3.13.0 D1 to the session layer. - On-disk credential file modes 0700/0600 (PR #87, S2) —
setup.mjsnow creates~/.ocpat mode 0700 and bothadmin-keyandocp.dbat mode 0600. An idempotentreconcileFileModes()call inserver.mjsstartup tightens any existing installation to these modes automatically on every launch, so existing prod boxes fix themselves without manualchmod. Before this fix, all three files were created at the process's default umask (typically world-readable 0644 / 0755), leaving plaintext credentials readable by other local users. /api/usagedefault scope = self; admin all-keys requires?all=true(PR #88, S3) — the usage endpoint now applies a least-privilege default: anonymous callers receive only their own rows, non-admin authenticated callers receive only their own rows, and admin callers receive only their own rows unless they explicitly pass?all=true. When?all=trueis used, an audit log line is emitted. Before this fix, any admin-token holder could silently enumerate usage data for every key on the server.
Behavior changes
- Breaking change for admin tooling:
/api/usageno longer returns all-keys data by default. Existing cron jobs, dashboards, or scripts that rely on the admin token seeing all-keys output must add?all=trueto their request URL after upgrading to v3.14.0. - File mode reconcile at server startup logs a one-line notice per path when mode is tightened (e.g.
[security] tightened ~/.ocp/ocp.db → 0600). No action is required from the operator; the reconcile is idempotent and silent when modes are already correct. sessionsMap key is now${keyName}|${conversationId}internally. No client-visible wire change — thesession_idfield in request/response is unchanged.
Verification
- Stress-test pass: 11/11 phases including S1/S2/S3 security regression checks (Phase E, I, J). 35-minute sustained run, 60 calls, 0 errors, 0 timeouts. RSS dropped 51→47 MB across the window. Per-key cache isolation, singleflight, cache_control bypass, quota enforcement, file-mode reconcile, and scope guard against escalation all verified against running code.
Governance
- All three PRs (#86, #87, #88) include the explicit
cli.js-citation-not-applicable disclaimer (per PR #75 pattern) since they are OCP-internal access-control, session-state, and file-permission changes with no correspondingcli.jsoperation to cite.
No new env vars / no public API surface change beyond the documented breaking change
This release adds no new env vars or endpoints. The only externally visible change is the /api/usage scope guard (breaking for admin all-keys consumers; see Behavior changes above).
v3.13.0 — 2026-05-07
Features (cache layer hardening)
- Per-key cache isolation (D1) — the cache key now includes the API key id, so distinct keys never share cache entries. Anonymous/unauthenticated callers share one
anonpool. Hash format upgraded tov2; legacy v1-format rows orphan and are reaped by the existing TTL cleanup interval (no migration script). cache_controlbypass (D2) — when a request carries an Anthropiccache_controlannotation (top-level or nested in a content array), OCP skips its own cache entirely. The caller is using Anthropic-side prompt caching deliberately, and OCP must not interfere. Acache_skipped{reason: cache_control_present}log line is emitted on bypass.- Chunked stream replay (D3) — when a streaming request hits the cache, the cached content is now emitted as multiple SSE chunks (80 codepoints/chunk, codepoint-safe via
Array.from()) instead of a single large delta. Multibyte characters (CJK / emoji) stay intact. - Singleflight stampede protection (D4) — concurrent identical cache-miss requests now share one upstream
cli.jsspawn instead of spawning N processes. Followers receive byte-identical responses to what the leader returns. All-or-nothing failure semantics: if the leader errors, all followers receive the same error. Streaming-path singleflight is explicitly out of scope (TODO left for follow-up).
Behavior changes
/cache/statsresponse now includes additive fieldsinflightandrequesters(current in-flight singleflight entries and total waiting callers). Existing fieldsentries,totalHits,sizeBytesare preserved unchanged.
Governance
- New ADR
docs/adr/0005-no-multi-provider.md: OCP stays single-provider (Anthropic viacli.jsspawn). Multi-provider gateway refactor explicitly out of scope; cache improvements are explicitly in scope. - Design spec for this release:
docs/superpowers/specs/2026-05-07-cache-upgrade-design.md.
No new env vars / no public API surface change
This release adds no new env vars or endpoints. All four improvements are internal correctness/concurrency upgrades to the existing CLAUDE_CACHE_TTL-gated cache layer. No client-observable wire shape change.
v3.12.0 — 2026-04-25
Features
- Streaming heartbeat — opt-in SSE comment frame (
: keepalive\n\n) emitted during silent windows on the streaming response. Controlled byCLAUDE_HEARTBEAT_INTERVALenv var (ms;0= disabled, default). Covers both pre-first-byte and mid-stream tool-use pauses. Addresses #47. See design doc. X-Accel-Buffering: noresponse header added to SSE responses so heartbeats survive nginx/Cloudflare default buffering.
Behavior changes
- SSE headers are now sent immediately after the claude CLI spawns successfully, not on first stdout byte. The rare "spawn succeeded but subprocess died before any byte" path now closes the SSE stream cleanly rather than returning a JSON error.
Config additions
| Variable | Default | Description |
|---|---|---|
CLAUDE_HEARTBEAT_INTERVAL |
0 (disabled) |
Interval in ms for SSE keepalive comment frames on streaming path. Resets on every real frame. |
v3.11.1 — 2026-04-21
Fixes
- Concurrency slot leak on subprocess timeout (#37). The request-timeout handler called
proc.kill("SIGTERM")without decrementingstats.activeRequests. A subprocess stuck in a syscall that ignored SIGTERM would hold its slot until (or beyond) the 5s SIGKILL escalation actually reaped it. Slot release is now wired toproc.once("exit", cleanup)so every termination path — normal close, error, SIGTERM, SIGKILL — releases the slot exactly once.
v3.11.0 — 2026-04-20
Features
ocp updatenow automatically syncs OpenClaw's registry with the latest models (scripts/sync-openclaw.mjs)- Server logs warn if OpenClaw registry drifts from models.json
Refactor
- models.json is now the single source of truth for model list
- server.mjs and setup.mjs derive MODEL_MAP/MODELS from models.json
- Adding a new model is now a one-file edit
Fixes
- OpenClaw's model dropdown now shows all 4 current models (opus-4-7, opus-4-6, sonnet-4-6, haiku-4.5) on existing installs after
ocp update. Previously setup.mjs only wrote the registry at install time.