diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bffb9..f6972c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,33 @@ All notable changes to OLP land here. Per `CLAUDE.md` release_kit overlay, this (empty — Phase 5 entries land here once Phase 5 opens) +## v0.4.4 — 2026-05-26 + +### D78 — `bin/olp-connect` stale-strings cleanup + README CDN-safe URL + repo-visibility flip + +Patch release on top of v0.4.3. Three small issues caught when running `olp-connect` for real on MacBook (D77 client-install verification): + +- **G11 fix (repo visibility).** Repo `dtzp555-max/olp` flipped from PRIVATE → PUBLIC during this session, closing the original G11 finding (`bash <(curl -fsSL .../main/bin/olp-connect)` returned 404 because anonymous curl can't fetch from private repos). README's `/main/` URL works going forward; GitHub's raw CDN may serve a stale 404 for `/main/` for ~5-15min after the visibility flip due to negative caching. D78 defends against this by adding a **tag-pinned URL (`/v0.4.4/bin/olp-connect`) as the primary recommendation in README**, with `/main/` listed as an alternative for trusted-head users. Tag-pinned URLs bypass the negative-cache because the tag ref was never queried while the repo was private. +- **G12 fix (`detect_openclaw` claimed plugin not shipped).** `bin/olp-connect`'s OpenClaw detection block said `"The OpenClaw OLP plugin (D71-D73) is NOT YET SHIPPED"` — but D71-D73 shipped `olp-plugin/` at v0.4.0. D78 replaces the stale text with real install instructions: `git clone` + `openclaw plugins install ./olp-plugin/` (or symlink), edit `~/.openclaw/openclaw.json` with a dedicated bot apiKey, restart gateway. Points at `docs/integrations/openclaw.md` for the full setup. +- **G13 fix (`olp-connect` self-version hardcoded literal).** Pre-D78 the script declared `OLP_CONNECT_VERSION="0.4.0-phase4"` as a hardcoded literal that nobody updated through v0.4.1 / v0.4.2 / v0.4.3 (the maintain-the-literal-per-release pattern is reliably forgotten). D78 derives the version at runtime from the sibling `package.json` via python3 — when the script is invoked from a checked-out repo, version resolves to the actual `package.json` value; when invoked via `curl … | bash` with no on-disk package.json next to it, falls back to `unknown`. Now `bash bin/olp-connect --version` prints `olp-connect 0.4.4` automatically with no manual touch needed at the next release. + +**Pre-publish audit.** Per `~/.cc-rules/docs/guides/pre-publish-audit.md` checklist (2026-05-26 session, before the visibility flip): +- Identity scrub: 0 hits (no personal names / hostnames / home paths / personal emails leaked into the working tree) +- Credential scrub: 0 real tokens — all `olp_` matches are placeholder (`olp_XXXX...`) or test fixtures (`olp_not-a-real-key-...`); gitleaks: "no leaks found" +- Git-history author emails: 78 commits, two emails (`dtzp555@gmail.com` local + `taodeng1977@gmail.com` GitHub-account squash-merges). Maintainer chose Option A (accept) — the GitHub-account email was already verified-public on the maintainer's GitHub profile, so the visibility flip exposes nothing new. + +**Test count:** 717 (v0.4.3) → 720 (v0.4.4). +3 D78 regression tests in Suite 36: +- 36v — pins absence of `NOT YET SHIPPED` text + presence of real install path +- 36w — pins runtime version derivation from package.json (hardcoded literal gone) +- 36x — pins README's tag-pinned-URL recommendation + +**Authority:** D77 MacBook client-install verification session (2026-05-26); `~/.cc-rules/docs/guides/pre-publish-audit.md`. Process learning: every README that includes a `curl | bash` install pattern should pin to a release tag (not `/main/`) for CDN-cache resilience. The /main/ form is correct for the long-tail (when no negative cache exists) but the tag-pinned form survives the visibility-flip transient + survives any future force-push to main. + +**Out of D78 scope:** +- F6 (doctor client-side vs server-side check separation) — Phase 5 ADR amendment. +- D75 reviewer P2-1 (ADR 0004 per-hop schema amendment) + P2-2 (defensive `typeof hopModel === 'string'` invariant) — both genuine follow-ups, neither blocking. +- `scripts/migrate-from-ocp.mjs` — Phase 7. + ## v0.4.3 — 2026-05-26 ### D76 — README install-path overhaul + `OLP_BIND` env + AI-driven install prompt + ADR 0011 amendment diff --git a/README.md b/README.md index 7c7551a..ceee6dd 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,10 @@ To let other devices on your home network use the same OLP server, you need TWO 2. **Onboard each family member's device** from THEIR machine: ```bash + # Pinned to a known-good release (recommended — survives GitHub raw CDN cache hiccups): + bash <(curl -fsSL https://raw.githubusercontent.com/dtzp555-max/olp/v0.4.4/bin/olp-connect) + + # OR latest from main (use after v0.4.4 + once you trust head): bash <(curl -fsSL https://raw.githubusercontent.com/dtzp555-max/olp/main/bin/olp-connect) ``` diff --git a/bin/olp-connect b/bin/olp-connect index cfb55c5..33ad62c 100755 --- a/bin/olp-connect +++ b/bin/olp-connect @@ -29,7 +29,35 @@ set -euo pipefail -OLP_CONNECT_VERSION="0.4.0-phase4" +# D78 (G13): derive version from package.json instead of hardcoding (was +# stuck at "0.4.0-phase4" through v0.4.1/v0.4.2/v0.4.3 because no one +# updated it). Look up package.json next to the script if available; +# fall back to "unknown" when running curl-piped (no on-disk package.json). +_resolve_version() { + local script_dir pkg + # When curl-piped (`curl ... | bash`), BASH_SOURCE[0] is empty → dirname + # yields "." → script_dir resolves to cwd. D78 reviewer P2-1 hardening: + # require the suffix-strip to actually fire (script_dir ENDED with /bin), + # otherwise we'd happily pick up an unrelated package.json from whatever + # directory the user happens to be in when piping. Belt-and-braces. + # ${BASH_SOURCE[0]:-} default-empty guards against `set -u` nounset error + # when invoked via `curl ... | bash` (no source file → BASH_SOURCE unset). + script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]:-}")" &>/dev/null && pwd)" + if [[ "$script_dir" != */bin ]]; then + echo "unknown" + return + fi + pkg="${script_dir%/bin}/package.json" + # D78 reviewer P2-2: pass $pkg via env var instead of -c interpolation + # so paths with apostrophes / shell metacharacters can't break the + # python invocation. Canonical layout is safe; this is defense-in-depth. + if [[ -f "$pkg" ]] && command -v python3 >/dev/null 2>&1; then + OLP_PKG_PATH="$pkg" python3 -c 'import json,os;print(json.load(open(os.environ["OLP_PKG_PATH"])).get("version","unknown"))' 2>/dev/null || echo "unknown" + else + echo "unknown" + fi +} +OLP_CONNECT_VERSION="$(_resolve_version)" show_version() { echo "olp-connect $OLP_CONNECT_VERSION" @@ -246,17 +274,28 @@ detect_aider() { fi } -# Detect OpenClaw. Per Phase 4 D71-D73 (NOT in this PR), olp will ship -# olp-plugin/ for OpenClaw with full Telegram/Discord /olp slash commands. -# Until that ships, we just announce detection and link. +# Detect OpenClaw. Phase 4 D71-D73 shipped olp-plugin/ as the OpenClaw +# gateway plugin for /olp Telegram + Discord slash commands. Point users +# at the install path. detect_openclaw() { if command -v openclaw &>/dev/null || [[ -f "$HOME/.openclaw/openclaw.json" ]]; then log_info "" log_info "Detected: OpenClaw" - log_info " The OpenClaw OLP plugin (D71-D73) is NOT YET SHIPPED." - log_info " When it ships, install with: openclaw plugin install olp" - log_info " For now, you can manually point OpenClaw at OLP via the OPENAI_BASE_URL" - log_info " env var (already written to your shell rc above)." + log_info " OLP ships an OpenClaw gateway plugin for /olp Telegram + Discord" + log_info " slash commands (status / usage / cache / models / providers /" + log_info " chain show / health / doctor). Read-only by design — no chat-side" + log_info " mutations." + log_info "" + log_info " Install the plugin (one-time, on the host running OpenClaw):" + log_info " git clone https://github.com/dtzp555-max/olp.git /tmp/olp-repo" + log_info " openclaw plugins install /tmp/olp-repo/olp-plugin" + log_info " # OR symlink: ln -sf /tmp/olp-repo/olp-plugin ~/.openclaw/extensions/olp" + log_info "" + log_info " Then edit ~/.openclaw/openclaw.json to set the plugin apiKey to a" + log_info " dedicated OLP key (NOT your owner key — create one via olp-keys" + log_info " keygen --name ). Restart OpenClaw gateway." + log_info "" + log_info " See docs/integrations/openclaw.md for full instructions." fi } diff --git a/package.json b/package.json index 86f5954..a8acbba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "olp", - "version": "0.4.3", + "version": "0.4.4", "description": "Personal multi-provider LLM proxy. Successor to OCP. One HTTP endpoint, multiple subscriptions behind it, automatic routing + fallback + caching.", "type": "module", "main": "server.mjs", diff --git a/test-features.mjs b/test-features.mjs index 67fe6f3..b6fce35 100644 --- a/test-features.mjs +++ b/test-features.mjs @@ -15398,4 +15398,45 @@ describe('Suite 36 — D74 v0.4.1 hotfix regression (maintainer review findings) assert.ok(/anonymous_key_advertised_with_lan_bind/.test(adrSrc), 'amendment must cite the startup-warn event name'); }); + + // ── D78 v0.4.4: G12 stale openclaw text + G13 self-version derived ────── + it('36v (G12) — olp-connect openclaw detection no longer claims plugin not shipped', () => { + // D71-D73 shipped olp-plugin/. Pre-D78 the script said "NOT YET SHIPPED" + // which misled MacBook client testing on 2026-05-26. Pin the corrected text. + const ocSrc = _readFileSyncS36(_joinS36(import.meta.dirname ?? process.cwd(), 'bin/olp-connect'), 'utf8'); + assert.ok(!/NOT YET SHIPPED/.test(ocSrc), + 'olp-connect must NOT claim openclaw plugin is unshipped (D71-D73 shipped it at v0.4.0)'); + assert.ok(/openclaw plugins install/.test(ocSrc) || /\.openclaw\/extensions\/olp/.test(ocSrc), + 'olp-connect openclaw detection must give a real install path'); + assert.ok(/docs\/integrations\/openclaw\.md/.test(ocSrc), + 'olp-connect must point at the openclaw integration doc'); + }); + + it('36w (G13) — olp-connect self-version derived from package.json (not hardcoded)', () => { + // Pre-D78 OLP_CONNECT_VERSION was a hardcoded literal "0.4.0-phase4" that + // nobody updated across v0.4.1/v0.4.2/v0.4.3. D78 derives at runtime + // from sibling package.json so the literal stays in sync automatically. + const ocSrc = _readFileSyncS36(_joinS36(import.meta.dirname ?? process.cwd(), 'bin/olp-connect'), 'utf8'); + // The old hardcoded literal must be gone + assert.ok(!/OLP_CONNECT_VERSION="0\.4\.0-phase4"/.test(ocSrc), + 'hardcoded "0.4.0-phase4" version literal must be gone'); + // The new derivation logic must reference package.json + assert.ok(/package\.json/.test(ocSrc) && /OLP_CONNECT_VERSION=/.test(ocSrc), + 'OLP_CONNECT_VERSION must derive from package.json'); + }); + + it('36x (D78) — README pins primary olp-connect curl URL to a release tag (CDN-cache-safe)', () => { + // G11 root cause: README's `bash <(curl ... /main/bin/olp-connect)` got + // bitten by GitHub raw CDN's negative-cache TTL when the repo flipped + // private->public. Tag-pinned URLs (...//bin/...) bypass that + // negative cache because the tag ref was never queried while private. + // D78: README presents the tag-pinned URL as the primary recommendation, + // with /main/ as an alternative for trusted-head users. + const readmePath = _joinS36(import.meta.dirname ?? process.cwd(), 'README.md'); + const readmeSrc = _readFileSyncS36(readmePath, 'utf8'); + assert.ok(/raw\.githubusercontent\.com\/dtzp555-max\/olp\/v\d+\.\d+\.\d+\/bin\/olp-connect/.test(readmeSrc), + 'README must include a release-tag-pinned olp-connect URL (e.g., /v0.4.4/bin/olp-connect)'); + assert.ok(/Pinned to a known-good release/.test(readmeSrc), + 'README must explain why the tag-pinned form is the primary recommendation'); + }); });