chore(release): v3.21.0 — TUI cleanup + client-tools/ToS docs + promotion plan (#145)

* refactor(tui): dead-code / footgun cleanup (A1/A2/A3)

ALIGNMENT.md Rule 2: infra-only, no new cli.js wire behavior.
No protocol, no endpoint, no credential changes.

A1 (session.mjs): delete resolveTuiEntrypointEnv() and its call site
+ the redundant env-strip block around the spawnSync call. The function
mutated a {env} object passed to spawnSync (tmux itself), but tmux does
NOT forward that env to the pane; the pane's claude gets its env ONLY
from the `env` prefix string built inside buildTuiCmd (verified live
2026-06-01). The spawnSync {env} is intentionally minimal now; only
env.HOME is retained (tmux binary reads it). All claude-specific vars
go via the buildTuiCmd prefix string, unchanged. Tests for the now-
deleted function removed; test count drops by 7 (expected).

A2 (transcript.mjs): delete encodeCwd() and transcriptPath() exports.
Production resolves transcripts exclusively via findTranscriptPath()
(glob by session-id); these two helpers carried a fragile path-encoding
rule used only by their own tests. grep confirms zero non-test importers.
Added a TODO comment near findTranscriptPath() noting a CI fixture-
contract test would make claude-schema drift fail loudly. Tests removed;
count drops by 2.

A3 (session.mjs + README): remove the CLAUDE_SKIP_PERMISSIONS branch
that pushed --dangerously-skip-permissions when OCP_TUI_FULL_TOOLS=1.
OCP_TUI_FULL_TOOLS=1 now always takes the --allowedTools path. Rationale:
claude v2.1.x shows an interactive bypass-acceptance screen that a
headless tmux TUI cannot answer — bricks the turn (tui_paste_not_landed
/ wallclock cap), not recoverable without a human. The working path is
--allowedTools + scratch-home settings.json additionalDirectories.
README OCP_TUI_FULL_TOOLS row updated to document the removal and the
correct alternative; CLAUDE_SKIP_PERMISSIONS row for the -p path is
unchanged (still used in server.mjs). Test updated: skip-permissions
case replaced with an assertion that --dangerously-skip-permissions is
absent from the full-tools command.

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

* docs: client-tools boundary, ToS honesty, promotion plan (B1/B2/B3)

ALIGNMENT.md Rule 2: docs-only, no new cli.js wire behavior.
No protocol, endpoint, or credential changes.

B1 (README): add 'Client-tools boundary' subsection under 'How It
Works'. Documents that OCP is a text-prompt bridge only — it does not
pass OpenAI tools/functions or Anthropic tool_use blocks to the client.
Clients receive assistant TEXT only; client-local tool execution is
not supported by design (bypassing cli.js = out of scope per
ALIGNMENT.md).

B2 (README): two updates to 'Why OCP?' and the LAN-sharing section.
(a) New bullet: OCP drives the official claude CLI as-is — no OAuth
token extraction, no binary patching, no protocol invention — so
traffic looks like genuine Claude Code (cc_entrypoint=cli).
(b) LAN-sharing paragraph strengthened: pooling one Claude subscription
across multiple distinct people may violate Anthropic's Consumer ToS
and risk account suspension by the abuse classifier. The defensible
framing is 'one person, your own devices'; friends/team sharing is
not. Replaces the softer 'account terms are your call' language.
Feature and auth-mode docs are unchanged.

B3 (docs/PROMOTION.md): new promotion strategy doc. Covers: goal
(polish + low-key OSS visibility, NOT growth-hacking given the live
ToS/billing risk), pre-requisites (stability first), honest ToS
disclosure requirement, items explicitly skipped (multi-backend
routing, gateway model-discovery — delegated to OLP; raw API
passthrough — ALIGNMENT.md scope), TUI toggle as billing-split
insurance, and low-key visibility actions. Framed as a recommendation
for the maintainer to review, not a committed plan.

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

* chore(release): v3.21.0 — TUI cleanup + docs honesty + promotion plan

ALIGNMENT.md Rule 2: release prep; no new cli.js wire behavior.

Bump version 3.20.1 → 3.21.0. CHANGELOG entry covers:
- A1/A2/A3 TUI dead-code removals (inert entrypoint-env path,
  test-only transcript helpers, headless-unusable skip-permissions)
- B1/B2/B3 docs (client-tools boundary, ToS honesty, Why-OCP posture,
  promotion plan)
- Previously-shipped v3.20.x items documented for completeness:
  spawn-home isolation, bounded concurrency queue + 429, ocp restart,
  ocp-plugin OpenClaw compat.

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

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dtzp555-max
2026-06-25 11:50:06 +10:00
committed by GitHub
co-authored by taodeng Claude Sonnet 4.6
parent 5aaab5ea28
commit 38da104b97
7 changed files with 161 additions and 132 deletions
+3 -70
View File
@@ -1340,23 +1340,12 @@ test("streamStringAsSSE empty content: role + stop + [DONE] only", () => {
});
// ── Suite: TUI transcript reader ────────────────────────────────────────
import { encodeCwd, transcriptPath, findTranscriptPath, parseTranscriptLines, isTerminalLine, extractLatestAssistantText, verifyEntrypoint, detectTuiUpstreamError } from "./lib/tui/transcript.mjs";
import { findTranscriptPath, parseTranscriptLines, isTerminalLine, extractLatestAssistantText, verifyEntrypoint, detectTuiUpstreamError } from "./lib/tui/transcript.mjs";
import { readFileSync as tuiReadFileSync, mkdtempSync as tuiMkdtemp0, mkdirSync as tuiMkdir0, writeFileSync as tuiWrite0 } from "node:fs";
import { tmpdir as tuiTmp0 } from "node:os";
console.log("\nTUI transcript — path formula:");
test("encodeCwd replaces every slash AND every dot with dash", () => {
// Verified live (claude v2.1.158): /home/u/.ocp-tui/work -> -home-u--ocp-tui-work
assert.equal(encodeCwd("/home/u/.ocp-tui/work"), "-home-u--ocp-tui-work");
assert.equal(encodeCwd("/tmp/tui-test"), "-tmp-tui-test"); // dot-free path still correct
});
test("transcriptPath composes HOME/.claude/projects/<enc>/<sid>.jsonl", () => {
assert.equal(
transcriptPath("/home/u", "/home/u/.ocp-tui/work", "abc-123"),
"/home/u/.claude/projects/-home-u--ocp-tui-work/abc-123.jsonl"
);
});
test("findTranscriptPath locates <sid>.jsonl across projects subdirs by UUID", () => {
const home = tuiMkdtemp0(`${tuiTmp0()}/tui-home-`);
const sid = "11111111-2222-3333-4444-555555555555";
@@ -1740,7 +1729,7 @@ test("buildTuiCmd shq-escapes a token containing shell metacharacters (no inject
test("buildTuiCmd OCP_TUI_FULL_TOOLS=1 grants -p-equivalent tool surface (single-user opt-in)", () => {
const save = { ...process.env };
const restore = () => {
for (const k of ["OCP_TUI_FULL_TOOLS", "CLAUDE_SKIP_PERMISSIONS", "CLAUDE_MCP_CONFIG", "CLAUDE_ALLOWED_TOOLS"]) {
for (const k of ["OCP_TUI_FULL_TOOLS", "CLAUDE_MCP_CONFIG", "CLAUDE_ALLOWED_TOOLS"]) {
if (k in save) process.env[k] = save[k]; else delete process.env[k];
}
};
@@ -1752,20 +1741,14 @@ test("buildTuiCmd OCP_TUI_FULL_TOOLS=1 grants -p-equivalent tool surface (single
// gate on: --allowedTools (default set incl Bash), MCP wall dropped
process.env.OCP_TUI_FULL_TOOLS = "1";
delete process.env.CLAUDE_SKIP_PERMISSIONS;
delete process.env.CLAUDE_MCP_CONFIG;
delete process.env.CLAUDE_ALLOWED_TOOLS;
const full = buildTuiCmd("/usr/bin/claude", "m", "s", "/home/u", "cli");
assert.ok(full.includes("--allowedTools") && full.includes("Bash"), "full-tools grants --allowedTools incl Bash");
assert.ok(!full.includes("--strict-mcp-config") && !/--disallowedTools/.test(full), "full-tools drops the MCP wall");
// skip-permissions supersedes --allowedTools
process.env.CLAUDE_SKIP_PERMISSIONS = "true";
const skip = buildTuiCmd("/usr/bin/claude", "m", "s", "/home/u", "cli");
assert.ok(skip.includes("--dangerously-skip-permissions") && !skip.includes("--allowedTools"), "skip-permissions honored");
assert.ok(!full.includes("--dangerously-skip-permissions"), "skip-permissions branch is removed (bricks headless TUI)");
// mcp-config threaded through
delete process.env.CLAUDE_SKIP_PERMISSIONS;
process.env.CLAUDE_MCP_CONFIG = "/tmp/mcp.json";
const mcp = buildTuiCmd("/usr/bin/claude", "m", "s", "/home/u", "cli");
assert.ok(/--mcp-config '\/tmp\/mcp.json'/.test(mcp), "mcp-config passed through (shq'd)");
@@ -1929,56 +1912,6 @@ test("resolveTuiHome: explicit OCP_TUI_HOME wins regardless of env token (back-c
assert.equal(resolveTuiHome({ realHome: "/home/u", configuredHome: "/custom/home", envTokenSet: false }), "/custom/home");
});
// ── resolveTuiEntrypointEnv ───────────────────────────────────────────────
import { resolveTuiEntrypointEnv } from "./lib/tui/session.mjs";
console.log("\nresolveTuiEntrypointEnv:");
test("mode 'cli' sets CLAUDE_CODE_ENTRYPOINT=cli", () => {
const env = {};
resolveTuiEntrypointEnv(env, "cli");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, "cli");
});
test("mode 'cli' overwrites an inherited CLAUDE_CODE_ENTRYPOINT value", () => {
const env = { CLAUDE_CODE_ENTRYPOINT: "sdk-cli" };
resolveTuiEntrypointEnv(env, "cli");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, "cli");
});
test("mode 'auto' deletes CLAUDE_CODE_ENTRYPOINT (leaves unset)", () => {
const env = {};
resolveTuiEntrypointEnv(env, "auto");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, undefined);
assert.ok(!Object.prototype.hasOwnProperty.call(env, "CLAUDE_CODE_ENTRYPOINT"));
});
test("mode 'auto' deletes an inherited CLAUDE_CODE_ENTRYPOINT value", () => {
const env = { CLAUDE_CODE_ENTRYPOINT: "sdk-cli" };
resolveTuiEntrypointEnv(env, "auto");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, undefined);
assert.ok(!Object.prototype.hasOwnProperty.call(env, "CLAUDE_CODE_ENTRYPOINT"));
});
test("mode 'off' leaves an inherited CLAUDE_CODE_ENTRYPOINT value untouched", () => {
const env = { CLAUDE_CODE_ENTRYPOINT: "sdk-cli" };
resolveTuiEntrypointEnv(env, "off");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, "sdk-cli");
});
test("mode 'off' with no inherited value leaves env unchanged", () => {
const env = { OTHER: "x" };
resolveTuiEntrypointEnv(env, "off");
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, undefined);
assert.equal(env.OTHER, "x");
});
test("default mode (no second arg) behaves like 'cli'", () => {
const env = { CLAUDE_CODE_ENTRYPOINT: "sdk-cli" };
resolveTuiEntrypointEnv(env);
assert.equal(env.CLAUDE_CODE_ENTRYPOINT, "cli");
});
// ── TUI concurrency limiter + drift observability (PR-B: audit C-4 / C-5) ──
import { TuiSemaphore, recordTuiEntrypoint, buildTuiHealthBlock } from "./lib/tui/semaphore.mjs";