@@ -303,6 +303,7 @@ ${chalk.bold("Environment Variables:")}
|
||||
AZURE_OPENAI_RESOURCE_NAME - Azure OpenAI resource name (alternative to base URL)
|
||||
AZURE_OPENAI_API_VERSION - Azure OpenAI API version (default: v1)
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME_MAP - Azure OpenAI model=deployment map (comma-separated)
|
||||
DEEPSEEK_API_KEY - DeepSeek API key
|
||||
GEMINI_API_KEY - Google Gemini API key
|
||||
GROQ_API_KEY - Groq API key
|
||||
CEREBRAS_API_KEY - Cerebras API key
|
||||
|
||||
@@ -98,10 +98,12 @@ const OpenAICompletionsCompatSchema = Type.Object({
|
||||
requiresToolResultName: Type.Optional(Type.Boolean()),
|
||||
requiresAssistantAfterToolResult: Type.Optional(Type.Boolean()),
|
||||
requiresThinkingAsText: Type.Optional(Type.Boolean()),
|
||||
requiresReasoningContentOnAssistantMessages: Type.Optional(Type.Boolean()),
|
||||
thinkingFormat: Type.Optional(
|
||||
Type.Union([
|
||||
Type.Literal("openai"),
|
||||
Type.Literal("openrouter"),
|
||||
Type.Literal("deepseek"),
|
||||
Type.Literal("zai"),
|
||||
Type.Literal("qwen"),
|
||||
Type.Literal("qwen-chat-template"),
|
||||
|
||||
@@ -17,6 +17,7 @@ export const defaultModelPerProvider: Record<KnownProvider, string> = {
|
||||
openai: "gpt-5.4",
|
||||
"azure-openai-responses": "gpt-5.4",
|
||||
"openai-codex": "gpt-5.5",
|
||||
deepseek: "deepseek-v4-pro",
|
||||
google: "gemini-3.1-pro-preview",
|
||||
"google-gemini-cli": "gemini-3.1-pro-preview",
|
||||
"google-antigravity": "gemini-3.1-pro-high",
|
||||
|
||||
@@ -183,6 +183,7 @@ const API_KEY_LOGIN_PROVIDERS: Record<string, string> = {
|
||||
[BEDROCK_PROVIDER_ID]: "Amazon Bedrock",
|
||||
"azure-openai-responses": "Azure OpenAI Responses",
|
||||
cerebras: "Cerebras",
|
||||
deepseek: "DeepSeek",
|
||||
fireworks: "Fireworks",
|
||||
google: "Google Gemini",
|
||||
"google-vertex": "Google Vertex AI",
|
||||
|
||||
@@ -17,65 +17,103 @@ function copyToX11Clipboard(options: NativeClipboardExecOptions): void {
|
||||
}
|
||||
}
|
||||
|
||||
export async function copyToClipboard(text: string): Promise<void> {
|
||||
// Always emit OSC 52 - works over SSH/mosh, harmless locally
|
||||
const encoded = Buffer.from(text).toString("base64");
|
||||
process.stdout.write(`\x1b]52;c;${encoded}\x07`);
|
||||
const MAX_OSC52_ENCODED_LENGTH = 100_000;
|
||||
|
||||
function isRemoteSession(env: NodeJS.ProcessEnv = process.env): boolean {
|
||||
return Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.MOSH_CONNECTION);
|
||||
}
|
||||
|
||||
function emitOsc52(text: string): boolean {
|
||||
const encoded = Buffer.from(text).toString("base64");
|
||||
if (encoded.length > MAX_OSC52_ENCODED_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
process.stdout.write(`\x1b]52;c;${encoded}\x07`);
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function copyToClipboard(text: string): Promise<void> {
|
||||
let copied = false;
|
||||
|
||||
// Prefer direct clipboard writes. Emitting OSC 52 first can make terminals
|
||||
// write the same native clipboard concurrently with the addon, and very large
|
||||
// OSC 52 payloads can desynchronize terminal rendering.
|
||||
try {
|
||||
if (clipboard) {
|
||||
await clipboard.setText(text);
|
||||
return;
|
||||
copied = true;
|
||||
}
|
||||
} catch {
|
||||
// Fall through to platform-specific clipboard tools.
|
||||
}
|
||||
|
||||
// Also try native tools (best effort for local sessions)
|
||||
const remote = isRemoteSession();
|
||||
if (copied && !remote) {
|
||||
return;
|
||||
}
|
||||
|
||||
const p = platform();
|
||||
const options: NativeClipboardExecOptions = { input: text, timeout: 5000, stdio: ["pipe", "ignore", "ignore"] };
|
||||
|
||||
try {
|
||||
if (p === "darwin") {
|
||||
execSync("pbcopy", options);
|
||||
} else if (p === "win32") {
|
||||
execSync("clip", options);
|
||||
} else {
|
||||
// Linux. Try Termux, Wayland, or X11 clipboard tools.
|
||||
if (process.env.TERMUX_VERSION) {
|
||||
try {
|
||||
execSync("termux-clipboard-set", options);
|
||||
return;
|
||||
} catch {
|
||||
// Fall back to Wayland or X11 tools.
|
||||
}
|
||||
}
|
||||
|
||||
const hasWaylandDisplay = Boolean(process.env.WAYLAND_DISPLAY);
|
||||
const hasX11Display = Boolean(process.env.DISPLAY);
|
||||
const isWayland = isWaylandSession();
|
||||
if (isWayland && hasWaylandDisplay) {
|
||||
try {
|
||||
// Verify wl-copy exists (spawn errors are async and won't be caught)
|
||||
execSync("which wl-copy", { stdio: "ignore" });
|
||||
// wl-copy with execSync hangs due to fork behavior; use spawn instead
|
||||
const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] });
|
||||
proc.stdin.on("error", () => {
|
||||
// Ignore EPIPE errors if wl-copy exits early
|
||||
});
|
||||
proc.stdin.write(text);
|
||||
proc.stdin.end();
|
||||
proc.unref();
|
||||
} catch {
|
||||
if (hasX11Display) {
|
||||
copyToX11Clipboard(options);
|
||||
if (!copied) {
|
||||
try {
|
||||
if (p === "darwin") {
|
||||
execSync("pbcopy", options);
|
||||
copied = true;
|
||||
} else if (p === "win32") {
|
||||
execSync("clip", options);
|
||||
copied = true;
|
||||
} else {
|
||||
// Linux. Try Termux, Wayland, or X11 clipboard tools.
|
||||
if (process.env.TERMUX_VERSION) {
|
||||
try {
|
||||
execSync("termux-clipboard-set", options);
|
||||
copied = true;
|
||||
} catch {
|
||||
// Fall back to Wayland or X11 tools.
|
||||
}
|
||||
}
|
||||
|
||||
if (!copied) {
|
||||
const hasWaylandDisplay = Boolean(process.env.WAYLAND_DISPLAY);
|
||||
const hasX11Display = Boolean(process.env.DISPLAY);
|
||||
const isWayland = isWaylandSession();
|
||||
if (isWayland && hasWaylandDisplay) {
|
||||
try {
|
||||
// Verify wl-copy exists (spawn errors are async and won't be caught)
|
||||
execSync("which wl-copy", { stdio: "ignore" });
|
||||
// wl-copy with execSync hangs due to fork behavior; use spawn instead
|
||||
const proc = spawn("wl-copy", [], { stdio: ["pipe", "ignore", "ignore"] });
|
||||
proc.stdin.on("error", () => {
|
||||
// Ignore EPIPE errors if wl-copy exits early
|
||||
});
|
||||
proc.stdin.write(text);
|
||||
proc.stdin.end();
|
||||
proc.unref();
|
||||
copied = true;
|
||||
} catch {
|
||||
if (hasX11Display) {
|
||||
copyToX11Clipboard(options);
|
||||
copied = true;
|
||||
}
|
||||
}
|
||||
} else if (hasX11Display) {
|
||||
copyToX11Clipboard(options);
|
||||
copied = true;
|
||||
}
|
||||
}
|
||||
} else if (hasX11Display) {
|
||||
copyToX11Clipboard(options);
|
||||
}
|
||||
} catch {
|
||||
// Fall through to OSC 52 fallback.
|
||||
}
|
||||
} catch {
|
||||
// Ignore - OSC 52 already emitted as fallback
|
||||
}
|
||||
|
||||
if (remote || !copied) {
|
||||
const osc52Copied = emitOsc52(text);
|
||||
copied = copied || osc52Copied;
|
||||
}
|
||||
|
||||
if (!copied) {
|
||||
throw new Error("Failed to copy to clipboard");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user