mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
6d4751f9837ef7a51a25b52f2aebc4a92e667c40
6
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> |
||
|
|
d291331998 |
fix(tui): never inject the host's CLAUDE.md / auto-memory into proxied turns (#4) (#132)
OCP is a PROXY, not a Claude Code session. The proxied client (OpenClaw / an IDE) owns its own context and memory — the HOST's CLAUDE.md and auto-memory must never leak into the agent OCP runs on the user's behalf. buildTuiCmd now adds CLAUDE_CODE_DISABLE_CLAUDE_MDS=1 + CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 to the pane-command env-prefix, unconditionally (proxy purity is not an opt-in). Diagnosis (per Iron Rule 3, diagnose-before-fix) of the earlier "suppression works in recon but not through OCP" gap: it was NEVER an env-delivery bug. The hosts simply have no CLAUDE.md to suppress — PI231 has no ~/.claude/CLAUDE.md, no ~/CLAUDE.md, no ~/.claude/memory, no cwd/walk-up CLAUDE.md. The earlier "recon worked" run was on a machine that DID have a global CLAUDE.md. Different machines, different CLAUDE.md presence — not a code gap. The ~35K one-line-prompt context is therefore the inherent floor (interactive system prompt + built-in tool schemas; MCP is already hard-disabled via --strict-mcp-config), not CLAUDE.md. Verified live 2026-06-02 with a behavioral marker (Iron Rule 2, evidence-first): - Planted /home/tlab/.ocp-tui/work/CLAUDE.md = "end every reply with QUACKMARKER_42". - BEFORE fix, through OCP: "Reply with: hello" -> "hello\n\nQUACKMARKER_42" (host CLAUDE.md obeyed = leaked). - AFTER fix, same marker file present, through OCP: -> "hello" (marker blocked). This proves both that the host CLAUDE.md was being injected AND that the env-prefix delivery of the disable flag reaches claude and stops it. Tests: buildTuiCmd is now exported; +2 regression guards (suppression present; version-pin/entrypoint/MCP-wall retained, auto-mode leaves entrypoint unset). 194 pass. Scope (Iron Rule 11): TUI path only. The -p path has an equivalent but GATED mechanism (CLAUDE_NO_CONTEXT). Making -p unconditionally proxy-pure is a separate decision, noted as a follow-up, not bundled here. ALIGNMENT.md: this changes lib/tui/session.mjs, not server.mjs, so the server.mjs hard requirements do not trigger. TUI is the ADR-0007 proxy-side execution bridge; the two flags are documented Claude Code env vars (the same two the -p path already uses), not invented endpoints — cli.js citation N/A under Rule 2. Closes #4 (TUI portion). Co-authored-by: dtzp555 <dtzp555@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
9568411bcb |
fix(tui): reliable large-prompt paste + version-robust turn detection (#130) (#131)
TUI mode hung ("stuck typing") on the OpenClaw agent's real (large, multi-line) prompts.
Three root causes, all fixed:
1. transcript.mjs — terminal detection only recognized {system, turn_duration}, which
claude 2.1.114 (the cloud host) does not emit → readTuiTranscript never saw completion
and ran to the 120s wallclock even though claude had answered. Now also treats an
{assistant} line with message.stop_reason ∈ {end_turn, stop_sequence, max_tokens} as
terminal (version-robust; stop_reason "tool_use" stays non-terminal, so tool turns are
not truncated — preserves the v3.17.1 narrowing).
2. session.mjs paste — send-keys -l "$(cat file)" delivers a large multi-line prompt's
newlines as separate key events (≈repeated Enter), so the prompt never lands. Replaced
with tmux load-buffer + paste-buffer -p (bracketed paste): atomic, no shell arg limit,
claude ingests it as one "[Pasted text]".
3. session.mjs verify — the old "placeholder-gone" heuristic false-positived on claude's
empty curly-quote placeholder, so Enter fired into an empty box (THE root cause of the
hang). tuiPromptLanded now trusts only positive signals ([Pasted text] indicator or the
prompt's own text); readiness-poll + paste-verify-poll + fast-fail (tui_paste_not_landed)
turns a 120s wallclock hang into a deterministic ~5s error.
Also: env delivered to the pane via an `env`-prefix on the pane command (tmux does NOT
forward spawnSync's env, and new-session -e needs tmux ≥3.2 while the cloud host runs 2.7),
carrying DISABLE_AUTOUPDATER (version pin) + CLAUDE_CODE_ENTRYPOINT labeling.
Validated live on both hosts: a 300-line / ~30KB prompt that previously hung now returns
in ~5-8s; small prompts ~4-5s. 192 tests pass (new: terminal-detection across schemas,
positive-signal verify incl. the curly-quote false-positive guard, paste predicates).
Scope discipline: an attempt to also strip claude's CLAUDE.md/auto-memory injection (context
reduction) was REVERTED — it didn't measurably reduce context through OCP, caused an
off-response, and is a separate concern. It will be its own measured branch.
ALIGNMENT.md: TUI is the ADR-0007 proxy-side execution bridge, not a forwarded Anthropic
operation, so a cli.js citation is N/A under Rule 2. No blacklisted tokens / port literals.
Independent fresh-context reviewer (opus): APPROVE WITH CHANGES (Iron Rule 10) — validated
the three fixes, caught that a per-session tmux socket would break the startup stale-session
reaper, and flagged the context-suppression scope-creep; both were reverted per the review.
Closes #130.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
aa1c65beb1 |
fix: TUI hardening — non-loopback LAN gate + assert cc_entrypoint (#115) (#122)
Two TUI-mode findings from the 2026-05-31 audit (ADR 0007):
1. The fail-loud LAN gate refused TUI boot only when CLAUDE_BIND === "0.0.0.0",
but binding to a concrete LAN IP, a Tailscale 100.x address, or IPv6 :: is
equally network-exposed and slipped through — letting any reachable peer drive
the operator's full-filesystem claude session. New isLoopbackBind() treats only
127.0.0.0/8 / ::1 / localhost / ::ffff:127.0.0.1 as safe; the gate now trips on
any non-loopback bind (OCP_TUI_ALLOW_LAN=1 escape hatch retained).
2. verifyEntrypoint() was implemented and unit-tested but never wired: readTuiTranscript
discarded the events and returned only text, so a silent degrade to the metered
sdk-cli (Agent SDK) billing pool — which still returns text but costs money — went
undetected. readTuiTranscript now returns { text, entrypoint }; runTuiTurn passes it
through; callClaudeTui logs a "tui_entrypoint_mismatch" warning when configured cli
mode yields a non-cli entrypoint (including null/unverified). callClaudeTui still
returns Promise<string>, so singleflight/cache/completion downstream is unchanged.
ALIGNMENT.md: TUI is a proxy-side execution-mode bridge (ADR 0007); this adds a local
bind classifier (startup gate) + a non-fatal log assertion of an existing classification
— no Anthropic operation forwarded, 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) — verified
isLoopbackBind via truth table (no exposed address misclassified as loopback — the
dangerous direction), the full string→{text,entrypoint} contract ripple across all
callers (Promise<string> preserved), the mismatch logic (warns on sdk-cli AND null in
cli mode, silent for auto/off, non-fatal), and ALIGNMENT N/A via a fetch/header grep.
Minors are process-only (this commit body; a future lib/ extraction of the test-mirrored
helper). npm test → 181 passed, 0 failed.
Closes #115.
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
05a984df89 |
fix: OCP code-audit P1+P2 hardening (crash bugs + multi-tenant gates) (#106)
* fix(daemon): P1-1 guard proc.stdin against EPIPE crash
In spawnClaudeProcess, attach an error listener on proc.stdin BEFORE
the write/end calls so an EPIPE (child closed stdin mid-write) is
swallowed and logged rather than thrown as an unhandled exception.
The existing proc.on("error") listener is on the ChildProcess object,
NOT on the stdin Writable — it does not catch stdin write errors.
Hardening per OCP code audit; entry-surface contract unchanged for
single-user default path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tui): P1-3 remove tool_use from isTerminalLine; only turn_duration is terminal
In interactive TUI mode, stop_reason=tool_use does NOT mean the turn is
complete. Claude handles the tool call internally and continues generating —
the transcript advances to another assistant entry. Treating tool_use as
terminal truncated tool-using turns mid-flight.
Only {type:"system", subtype:"turn_duration"} is the authoritative
completion marker (claude CLI v2.1.157+ interactive session transcript).
Updated two unit tests that previously asserted tool_use → true; they now
assert false (the correct behaviour). The real-fixture terminal detection
test is unaffected because the fixture uses turn_duration.
Hardening per OCP code audit; TUI path behaviour fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tui): P2-5 add -l (literal) flag to send-keys prompt paste
A prompt that equals a tmux key token (e.g. "C-c", "Escape") would be
interpreted as that key binding rather than typed as literal text.
The -l flag forces literal character-by-character input. The separate
Enter key event afterward deliberately omits -l so tmux sends a real
carriage-return keypress to submit the prompt line.
Authority: tmux send-keys(1) § -l flag. Hardening per OCP code audit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(audit): String-coerce parsed.error + stale tool_use comment (review fast-follow)
Folds the 2 minor findings from the independent review of the audit fixes:
- String(parsed.error) before .slice/message in callClaude + callClaudeStreaming
(defensive: claude could emit a non-string result/error_message).
- correct the readTuiTranscript comment that still listed tool_use as terminal.
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>
|
||
|
|
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> |