From b689180717e6f6e6ecd14ef02cad01b30d8cdeca Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Sat, 8 Aug 2026 14:16:22 +0300 Subject: [PATCH] fix(keys): snippets survive being pasted into real zsh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The copyable text opened with a "# hint" line — but default interactive zsh has interactivecomments off, so the line was parsed as a command, and the "!" inside «(перезапишет!)» fired history expansion: pasting the OpenCode snippet died with "zsh: event not found: )" before doing anything. Hints now live in the UI above the snippet; the clipboard gets commands only. Verified by piping the actual rendered snippet through an interactive zsh: the config file lands intact. --- web/js/views/keys.js | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/web/js/views/keys.js b/web/js/views/keys.js index 6005236..6c1a1cb 100644 --- a/web/js/views/keys.js +++ b/web/js/views/keys.js @@ -62,14 +62,12 @@ export async function renderKeys() { // Harness auto-setup scripts first (the gateway speaks OpenAI at /v1/* and // Anthropic at /v1/messages, so Claude Code connects natively), SDKs after. const buildSnips = (osKind) => osKind === "ps" ? { - "Claude Code": `# ${t("keys.h.claude")} -$env:ANTHROPIC_BASE_URL = "${origin}" + "Claude Code": `$env:ANTHROPIC_BASE_URL = "${origin}" $env:ANTHROPIC_AUTH_TOKEN = "${key}" $env:ANTHROPIC_MODEL = "cortiq-auto" claude`, - Codex: `# ${t("keys.h.codex")} -New-Item -ItemType Directory -Force "$HOME\\.codex" | Out-Null + Codex: `New-Item -ItemType Directory -Force "$HOME\\.codex" | Out-Null Add-Content "$HOME\\.codex\\config.toml" @' model = "cortiq-auto" model_provider = "cortiq" @@ -83,8 +81,7 @@ wire_api = "chat" $env:CORTIQ_API_KEY = "${key}" codex`, - OpenCode: `# ${t("keys.h.opencode")} -New-Item -ItemType Directory -Force "$HOME\\.config\\opencode" | Out-Null + OpenCode: `New-Item -ItemType Directory -Force "$HOME\\.config\\opencode" | Out-Null Set-Content "$HOME\\.config\\opencode\\opencode.json" @' { "$schema": "https://opencode.ai/config.json", @@ -100,8 +97,7 @@ Set-Content "$HOME\\.config\\opencode\\opencode.json" @' '@ opencode`, - Continue: `# ${t("keys.h.continue")} -New-Item -ItemType Directory -Force "$HOME\\.continue" | Out-Null + Continue: `New-Item -ItemType Directory -Force "$HOME\\.continue" | Out-Null Add-Content "$HOME\\.continue\\config.yaml" @' models: - name: Cortiq Auto @@ -122,14 +118,12 @@ ${t("keys.h.cline2")}`, -H "Content-Type: application/json" \` -d '{"model":"cortiq-auto","messages":[{"role":"user","content":"Привет!"}]}'`, } : { - "Claude Code": `# ${t("keys.h.claude")} -export ANTHROPIC_BASE_URL="${origin}" + "Claude Code": `export ANTHROPIC_BASE_URL="${origin}" export ANTHROPIC_AUTH_TOKEN="${key}" export ANTHROPIC_MODEL="cortiq-auto" claude`, - Codex: `# ${t("keys.h.codex")} -mkdir -p ~/.codex && cat >> ~/.codex/config.toml <<'EOF' + Codex: `mkdir -p ~/.codex && cat >> ~/.codex/config.toml <<'EOF' model = "cortiq-auto" model_provider = "cortiq" @@ -142,8 +136,7 @@ EOF export CORTIQ_API_KEY="${key}" codex`, - OpenCode: `# ${t("keys.h.opencode")} -mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF' + OpenCode: `mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF' { "$schema": "https://opencode.ai/config.json", "provider": { @@ -158,8 +151,7 @@ mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF' EOF opencode`, - Continue: `# ${t("keys.h.continue")} -mkdir -p ~/.continue && cat >> ~/.continue/config.yaml <<'EOF' + Continue: `mkdir -p ~/.continue && cat >> ~/.continue/config.yaml <<'EOF' models: - name: Cortiq Auto provider: openai @@ -205,7 +197,19 @@ console.log(resp.choices[0].message.content);`, } rebuildSnips(); let cur = "Claude Code"; + // per-harness hint lives OUTSIDE the copyable text: a leading "# comment" + // in a pasted snippet is not a comment in default interactive zsh, and a + // "!" inside it triggers history expansion ("event not found") + const HINTS = { + "Claude Code": () => t("keys.h.claude"), + Codex: () => t("keys.h.codex"), + OpenCode: () => t("keys.h.opencode"), + Continue: () => t("keys.h.continue"), + }; + const hintHost = h("div", { class: "muted small", style: "margin-bottom:6px" }); + const paintHint = () => { hintHost.textContent = HINTS[cur] ? HINTS[cur]() : ""; }; const pre = h("pre", { class: "conv-log snippet" }, snips[cur]); + paintHint(); const seg = h( "div", { class: "seg" }, @@ -213,6 +217,7 @@ console.log(resp.choices[0].message.content);`, h("button", { class: (i === 0 ? "active" : "") + (["curl", "python", "javascript"].includes(k) ? " sdk" : ""), "data-snip": k, onClick: (e) => { cur = k; pre.textContent = snips[k]; + paintHint(); protoWarn.style.display = k === "Claude Code" && protoWarn.childNodes.length ? "" : "none"; paintStatus(); seg.querySelectorAll("button").forEach((b) => b.classList.remove("active")); @@ -321,6 +326,7 @@ console.log(resp.choices[0].message.content);`, h("div", { class: "muted small", style: "margin-bottom:8px" }, lastCreatedKey ? t("keys.snip.withKey") : openMode ? t("keys.snip.openMode") : t("keys.snip.hint")), h("div", { class: "flex", style: "gap:8px; margin-bottom:8px; flex-wrap:wrap" }, seg, h("div", { class: "grow" }), keyIn, applyBtn, osSeg, copy), + hintHost, pre, statusHost, protoWarn,