fix(keys): snippets survive being pasted into real zsh
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.
This commit is contained in:
Vendored
+22
-16
@@ -62,14 +62,12 @@ export async function renderKeys() {
|
|||||||
// Harness auto-setup scripts first (the gateway speaks OpenAI at /v1/* and
|
// Harness auto-setup scripts first (the gateway speaks OpenAI at /v1/* and
|
||||||
// Anthropic at /v1/messages, so Claude Code connects natively), SDKs after.
|
// Anthropic at /v1/messages, so Claude Code connects natively), SDKs after.
|
||||||
const buildSnips = (osKind) => osKind === "ps" ? {
|
const buildSnips = (osKind) => osKind === "ps" ? {
|
||||||
"Claude Code": `# ${t("keys.h.claude")}
|
"Claude Code": `$env:ANTHROPIC_BASE_URL = "${origin}"
|
||||||
$env:ANTHROPIC_BASE_URL = "${origin}"
|
|
||||||
$env:ANTHROPIC_AUTH_TOKEN = "${key}"
|
$env:ANTHROPIC_AUTH_TOKEN = "${key}"
|
||||||
$env:ANTHROPIC_MODEL = "cortiq-auto"
|
$env:ANTHROPIC_MODEL = "cortiq-auto"
|
||||||
|
|
||||||
claude`,
|
claude`,
|
||||||
Codex: `# ${t("keys.h.codex")}
|
Codex: `New-Item -ItemType Directory -Force "$HOME\\.codex" | Out-Null
|
||||||
New-Item -ItemType Directory -Force "$HOME\\.codex" | Out-Null
|
|
||||||
Add-Content "$HOME\\.codex\\config.toml" @'
|
Add-Content "$HOME\\.codex\\config.toml" @'
|
||||||
model = "cortiq-auto"
|
model = "cortiq-auto"
|
||||||
model_provider = "cortiq"
|
model_provider = "cortiq"
|
||||||
@@ -83,8 +81,7 @@ wire_api = "chat"
|
|||||||
$env:CORTIQ_API_KEY = "${key}"
|
$env:CORTIQ_API_KEY = "${key}"
|
||||||
|
|
||||||
codex`,
|
codex`,
|
||||||
OpenCode: `# ${t("keys.h.opencode")}
|
OpenCode: `New-Item -ItemType Directory -Force "$HOME\\.config\\opencode" | Out-Null
|
||||||
New-Item -ItemType Directory -Force "$HOME\\.config\\opencode" | Out-Null
|
|
||||||
Set-Content "$HOME\\.config\\opencode\\opencode.json" @'
|
Set-Content "$HOME\\.config\\opencode\\opencode.json" @'
|
||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.json",
|
"$schema": "https://opencode.ai/config.json",
|
||||||
@@ -100,8 +97,7 @@ Set-Content "$HOME\\.config\\opencode\\opencode.json" @'
|
|||||||
'@
|
'@
|
||||||
|
|
||||||
opencode`,
|
opencode`,
|
||||||
Continue: `# ${t("keys.h.continue")}
|
Continue: `New-Item -ItemType Directory -Force "$HOME\\.continue" | Out-Null
|
||||||
New-Item -ItemType Directory -Force "$HOME\\.continue" | Out-Null
|
|
||||||
Add-Content "$HOME\\.continue\\config.yaml" @'
|
Add-Content "$HOME\\.continue\\config.yaml" @'
|
||||||
models:
|
models:
|
||||||
- name: Cortiq Auto
|
- name: Cortiq Auto
|
||||||
@@ -122,14 +118,12 @@ ${t("keys.h.cline2")}`,
|
|||||||
-H "Content-Type: application/json" \`
|
-H "Content-Type: application/json" \`
|
||||||
-d '{"model":"cortiq-auto","messages":[{"role":"user","content":"Привет!"}]}'`,
|
-d '{"model":"cortiq-auto","messages":[{"role":"user","content":"Привет!"}]}'`,
|
||||||
} : {
|
} : {
|
||||||
"Claude Code": `# ${t("keys.h.claude")}
|
"Claude Code": `export ANTHROPIC_BASE_URL="${origin}"
|
||||||
export ANTHROPIC_BASE_URL="${origin}"
|
|
||||||
export ANTHROPIC_AUTH_TOKEN="${key}"
|
export ANTHROPIC_AUTH_TOKEN="${key}"
|
||||||
export ANTHROPIC_MODEL="cortiq-auto"
|
export ANTHROPIC_MODEL="cortiq-auto"
|
||||||
|
|
||||||
claude`,
|
claude`,
|
||||||
Codex: `# ${t("keys.h.codex")}
|
Codex: `mkdir -p ~/.codex && cat >> ~/.codex/config.toml <<'EOF'
|
||||||
mkdir -p ~/.codex && cat >> ~/.codex/config.toml <<'EOF'
|
|
||||||
model = "cortiq-auto"
|
model = "cortiq-auto"
|
||||||
model_provider = "cortiq"
|
model_provider = "cortiq"
|
||||||
|
|
||||||
@@ -142,8 +136,7 @@ EOF
|
|||||||
export CORTIQ_API_KEY="${key}"
|
export CORTIQ_API_KEY="${key}"
|
||||||
|
|
||||||
codex`,
|
codex`,
|
||||||
OpenCode: `# ${t("keys.h.opencode")}
|
OpenCode: `mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF'
|
||||||
mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF'
|
|
||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.json",
|
"$schema": "https://opencode.ai/config.json",
|
||||||
"provider": {
|
"provider": {
|
||||||
@@ -158,8 +151,7 @@ mkdir -p ~/.config/opencode && cat > ~/.config/opencode/opencode.json <<'EOF'
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
opencode`,
|
opencode`,
|
||||||
Continue: `# ${t("keys.h.continue")}
|
Continue: `mkdir -p ~/.continue && cat >> ~/.continue/config.yaml <<'EOF'
|
||||||
mkdir -p ~/.continue && cat >> ~/.continue/config.yaml <<'EOF'
|
|
||||||
models:
|
models:
|
||||||
- name: Cortiq Auto
|
- name: Cortiq Auto
|
||||||
provider: openai
|
provider: openai
|
||||||
@@ -205,7 +197,19 @@ console.log(resp.choices[0].message.content);`,
|
|||||||
}
|
}
|
||||||
rebuildSnips();
|
rebuildSnips();
|
||||||
let cur = "Claude Code";
|
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]);
|
const pre = h("pre", { class: "conv-log snippet" }, snips[cur]);
|
||||||
|
paintHint();
|
||||||
const seg = h(
|
const seg = h(
|
||||||
"div",
|
"div",
|
||||||
{ class: "seg" },
|
{ 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) => {
|
h("button", { class: (i === 0 ? "active" : "") + (["curl", "python", "javascript"].includes(k) ? " sdk" : ""), "data-snip": k, onClick: (e) => {
|
||||||
cur = k;
|
cur = k;
|
||||||
pre.textContent = snips[k];
|
pre.textContent = snips[k];
|
||||||
|
paintHint();
|
||||||
protoWarn.style.display = k === "Claude Code" && protoWarn.childNodes.length ? "" : "none";
|
protoWarn.style.display = k === "Claude Code" && protoWarn.childNodes.length ? "" : "none";
|
||||||
paintStatus();
|
paintStatus();
|
||||||
seg.querySelectorAll("button").forEach((b) => b.classList.remove("active"));
|
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" },
|
h("div", { class: "muted small", style: "margin-bottom:8px" },
|
||||||
lastCreatedKey ? t("keys.snip.withKey") : openMode ? t("keys.snip.openMode") : t("keys.snip.hint")),
|
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),
|
h("div", { class: "flex", style: "gap:8px; margin-bottom:8px; flex-wrap:wrap" }, seg, h("div", { class: "grow" }), keyIn, applyBtn, osSeg, copy),
|
||||||
|
hintHost,
|
||||||
pre,
|
pre,
|
||||||
statusHost,
|
statusHost,
|
||||||
protoWarn,
|
protoWarn,
|
||||||
|
|||||||
Reference in New Issue
Block a user