@@ -83,22 +83,17 @@ export async function copyTextToClipboard(
8383 }
8484
8585 try {
86- // Try OSC52 first (works over SSH/headless), then fallback to platform tools
87- if ( ! tryCopyViaOsc52 ( text ) ) {
88- const { execSync } = require ( 'child_process' ) as typeof import ( 'child_process' )
89- const opts = { input : text , stdio : [ 'pipe' , 'ignore' , 'ignore' ] as ( 'pipe' | 'ignore' ) [ ] }
90-
91- if ( process . platform === 'darwin' ) {
92- execSync ( 'pbcopy' , opts )
93- } else if ( process . platform === 'linux' ) {
94- try {
95- execSync ( 'xclip -selection clipboard' , opts )
96- } catch {
97- execSync ( 'xsel --clipboard --input' , opts )
98- }
99- } else if ( process . platform === 'win32' ) {
100- execSync ( 'clip' , opts )
101- }
86+ let copied : boolean
87+ if ( isRemoteSession ( ) ) {
88+ // Remote/SSH: prefer OSC 52 (copies to client terminal's clipboard)
89+ copied = tryCopyViaOsc52 ( text ) || tryCopyViaPlatformTool ( text )
90+ } else {
91+ // Local: prefer platform tools (reliable with tmux), OSC 52 as fallback
92+ copied = tryCopyViaPlatformTool ( text ) || tryCopyViaOsc52 ( text )
93+ }
94+
95+ if ( ! copied ) {
96+ throw new Error ( 'No clipboard method available' )
10297 }
10398
10499 if ( ! suppressGlobalMessage ) {
@@ -137,6 +132,35 @@ export function clearClipboardMessage() {
137132// because the client terminal handles clipboard. Format: ESC ] 52 ; c ; <base64> BEL
138133// tmux/screen require passthrough wrapping to forward the sequence.
139134
135+ function isRemoteSession ( ) : boolean {
136+ const env = getCliEnv ( )
137+ return ! ! ( env . SSH_CLIENT || env . SSH_TTY || env . SSH_CONNECTION )
138+ }
139+
140+ function tryCopyViaPlatformTool ( text : string ) : boolean {
141+ const { execSync } = require ( 'child_process' ) as typeof import ( 'child_process' )
142+ const opts = { input : text , stdio : [ 'pipe' , 'ignore' , 'ignore' ] as ( 'pipe' | 'ignore' ) [ ] }
143+
144+ try {
145+ if ( process . platform === 'darwin' ) {
146+ execSync ( 'pbcopy' , opts )
147+ } else if ( process . platform === 'linux' ) {
148+ try {
149+ execSync ( 'xclip -selection clipboard' , opts )
150+ } catch {
151+ execSync ( 'xsel --clipboard --input' , opts )
152+ }
153+ } else if ( process . platform === 'win32' ) {
154+ execSync ( 'clip' , opts )
155+ } else {
156+ return false
157+ }
158+ return true
159+ } catch {
160+ return false
161+ }
162+ }
163+
140164// 32KB is safe for all environments (tmux is the strictest)
141165const OSC52_MAX_PAYLOAD = 32_000
142166
0 commit comments