@@ -29,8 +29,9 @@ function firstText(...values) {
2929 return '' ;
3030}
3131
32- function kittyBin ( config = { } ) {
33- return text ( config . kittyBin || process . env . GUARDEX_KITTY_BIN , DEFAULT_KITTY_BIN ) ;
32+ function kittyBin ( config = { } , options = { } ) {
33+ const envValue = options . allowEnv ? process . env . GUARDEX_KITTY_BIN : '' ;
34+ return text ( config . kittyBin || envValue , DEFAULT_KITTY_BIN ) ;
3435}
3536
3637function commandShape ( args , config = { } ) {
@@ -40,31 +41,128 @@ function commandShape(args, config = {}) {
4041 } ;
4142}
4243
43- function appendShellCommand ( args , command ) {
44- const normalized = text ( command ) ;
45- if ( normalized ) {
46- args . push ( '--' , 'sh' , '-lc' , normalized ) ;
44+ function commandShapeWithEnv ( args , config = { } ) {
45+ return {
46+ cmd : kittyBin ( config , { allowEnv : true } ) ,
47+ args,
48+ } ;
49+ }
50+
51+ function appendOption ( args , flag , value ) {
52+ const normalized = text ( value ) ;
53+ if ( normalized ) args . push ( flag , normalized ) ;
54+ return args ;
55+ }
56+
57+ function normalizeEnvEntries ( env = { } ) {
58+ if ( ! env ) return [ ] ;
59+ if ( Array . isArray ( env ) ) {
60+ return env . map ( ( entry ) => {
61+ if ( Array . isArray ( entry ) ) {
62+ const key = requireText ( entry [ 0 ] , 'kitty env key' ) ;
63+ const value = entry . length > 1 && entry [ 1 ] !== undefined && entry [ 1 ] !== null ? String ( entry [ 1 ] ) : '' ;
64+ return `${ key } =${ value } ` ;
65+ }
66+ return requireText ( entry , 'kitty env' ) ;
67+ } ) ;
68+ }
69+ if ( typeof env !== 'object' ) {
70+ throw new TypeError ( 'kitty env must be an object, array, or undefined' ) ;
71+ }
72+ return Object . keys ( env )
73+ . sort ( )
74+ . map ( ( key ) => `${ requireText ( key , 'kitty env key' ) } =${ env [ key ] === undefined || env [ key ] === null ? '' : String ( env [ key ] ) } ` ) ;
75+ }
76+
77+ function appendEnv ( args , env ) {
78+ for ( const entry of normalizeEnvEntries ( env ) ) {
79+ args . push ( '--env' , entry ) ;
4780 }
4881 return args ;
4982}
5083
84+ function normalizeCommandArgv ( options = { } ) {
85+ const commandArgv = options . argv || options . commandArgv || ( Array . isArray ( options . command ) ? options . command : undefined ) ;
86+ if ( commandArgv === undefined || commandArgv === null ) return [ ] ;
87+ if ( ! Array . isArray ( commandArgv ) ) {
88+ throw new TypeError ( 'kitty command argv must be an array' ) ;
89+ }
90+ return commandArgv . map ( ( arg ) => {
91+ if ( arg === undefined || arg === null ) {
92+ throw new TypeError ( 'kitty command argv values must be strings' ) ;
93+ }
94+ return String ( arg ) ;
95+ } ) ;
96+ }
97+
98+ function appendCommandArgv ( args , options = { } ) {
99+ const commandArgv = normalizeCommandArgv ( options ) ;
100+ if ( commandArgv . length > 0 ) args . push ( '--' , ...commandArgv ) ;
101+ return args ;
102+ }
103+
104+ function shellCommandArgv ( command ) {
105+ const normalized = text ( command ) ;
106+ return normalized ? [ 'sh' , '-lc' , normalized ] : [ ] ;
107+ }
108+
109+ function launchTitle ( options = { } ) {
110+ const session = options . session && typeof options . session === 'object' ? options . session : { } ;
111+ return firstText (
112+ options . title ,
113+ options . control || options . role === 'control' ? DEFAULT_COCKPIT_TITLE : '' ,
114+ options . agent || options . role === 'agent' || options . session ? agentTitle ( session ) : '' ,
115+ options . terminal || options . role === 'terminal' ? DEFAULT_TERMINAL_TITLE : '' ,
116+ ) ;
117+ }
118+
119+ function launchCwd ( options = { } ) {
120+ const session = options . session && typeof options . session === 'object' ? options . session : { } ;
121+ return firstText ( options . cwd , options . repoRoot , options . worktree , session . worktreePath , session . path ) ;
122+ }
123+
124+ function buildKittyLaunchCommand ( options = { } ) {
125+ const args = [ '@' , 'launch' ] ;
126+ const type = text ( options . type , 'window' ) ;
127+ const location = firstText ( options . location , options . pane ? 'vsplit' : '' ) ;
128+ const cwd = launchCwd ( options ) ;
129+ const title = launchTitle ( options ) ;
130+
131+ if ( type ) args . push ( `--type=${ type } ` ) ;
132+ if ( location ) args . push ( `--location=${ location } ` ) ;
133+ appendOption ( args , '--cwd' , cwd ) ;
134+ appendOption ( args , '--title' , title ) ;
135+ appendEnv ( args , options . env ) ;
136+ appendCommandArgv ( args , options ) ;
137+
138+ const shape = commandShape ( args , options ) ;
139+ if ( Object . prototype . hasOwnProperty . call ( options , 'input' ) ) {
140+ shape . input = options . input === undefined || options . input === null ? '' : String ( options . input ) ;
141+ }
142+ return shape ;
143+ }
144+
145+ function buildKittyLsCommand ( options = { } ) {
146+ return commandShape ( [ '@' , 'ls' ] , options ) ;
147+ }
148+
149+ function buildKittyVersionCommand ( options = { } ) {
150+ return commandShape ( [ '--version' ] , options ) ;
151+ }
152+
51153function buildAvailabilityCommand ( config = { } ) {
52- return commandShape ( [ '@' , 'ls' ] , config ) ;
154+ return commandShapeWithEnv ( buildKittyLsCommand ( ) . args , config ) ;
53155}
54156
55157function buildOpenCockpitLayoutCommand ( options = { } , config = { } ) {
56158 const repoRoot = requireText ( options . repoRoot , 'kitty cockpit repoRoot' ) ;
57- const args = [
58- '@' ,
59- 'launch' ,
60- '--type=window' ,
61- '--cwd' ,
62- repoRoot ,
63- '--title' ,
64- text ( options . title , DEFAULT_COCKPIT_TITLE ) ,
65- ] ;
66- appendShellCommand ( args , options . command ) ;
67- return commandShape ( args , config ) ;
159+ return buildKittyLaunchCommand ( {
160+ role : 'control' ,
161+ cwd : repoRoot ,
162+ title : text ( options . title , DEFAULT_COCKPIT_TITLE ) ,
163+ argv : shellCommandArgv ( options . command ) ,
164+ kittyBin : kittyBin ( config , { allowEnv : true } ) ,
165+ } ) ;
68166}
69167
70168function agentTitle ( session = { } , title ) {
@@ -82,57 +180,74 @@ function agentTitle(session = {}, title) {
82180function buildLaunchAgentPaneCommand ( options = { } , config = { } ) {
83181 const session = options . session && typeof options . session === 'object' ? options . session : { } ;
84182 const cwd = requireText ( firstText ( options . worktree , session . worktreePath , session . path ) , 'kitty agent worktree' ) ;
85- const args = [
86- '@' ,
87- 'launch' ,
88- '--type=window' ,
89- '--location=vsplit' ,
90- '--cwd' ,
183+ return buildKittyLaunchCommand ( {
184+ role : 'agent' ,
185+ pane : true ,
91186 cwd,
92- '--title' ,
93- agentTitle ( session , options . title ) ,
94- ] ;
95- appendShellCommand ( args , options . command ) ;
96- return commandShape ( args , config ) ;
187+ title : agentTitle ( session , options . title ) ,
188+ argv : shellCommandArgv ( options . command ) ,
189+ kittyBin : kittyBin ( config , { allowEnv : true } ) ,
190+ } ) ;
97191}
98192
99193function buildLaunchTerminalPaneCommand ( options = { } , config = { } ) {
100194 const cwd = requireText ( options . cwd , 'kitty terminal cwd' ) ;
101- const args = [
102- '@' ,
103- 'launch' ,
104- '--type=window' ,
105- '--location=vsplit' ,
106- '--cwd' ,
195+ return buildKittyLaunchCommand ( {
196+ role : 'terminal' ,
197+ pane : true ,
107198 cwd,
108- '--title' ,
109- text ( options . title , DEFAULT_TERMINAL_TITLE ) ,
110- ] ;
111- appendShellCommand ( args , options . command ) ;
112- return commandShape ( args , config ) ;
199+ title : text ( options . title , DEFAULT_TERMINAL_TITLE ) ,
200+ argv : shellCommandArgv ( options . command ) ,
201+ kittyBin : kittyBin ( config , { allowEnv : true } ) ,
202+ } ) ;
113203}
114204
115- function targetId ( target ) {
116- const raw = target && typeof target === 'object'
117- ? firstText ( target . id , target . windowId , target . paneId , target . target )
118- : text ( target ) ;
119- return requireText ( raw , 'kitty target id' ) ;
205+ function targetMatch ( target ) {
206+ if ( target && typeof target === 'object' ) {
207+ const explicitMatch = firstText ( target . match , target . kittyMatch ) ;
208+ if ( explicitMatch ) return explicitMatch ;
209+
210+ const id = firstText ( target . id , target . windowId , target . kittyWindowId , target . paneId , target . target ) ;
211+ if ( id ) return `id:${ id } ` ;
212+
213+ const title = firstText ( target . title , target . windowTitle , target . kittyTitle ) ;
214+ if ( title ) return `title:${ title } ` ;
215+ } else {
216+ const id = text ( target ) ;
217+ if ( id ) return `id:${ id } ` ;
218+ }
219+ throw new TypeError ( 'kitty target must include id, title, or match' ) ;
120220}
121221
122- function targetMatch ( target ) {
123- return `id:${ targetId ( target ) } ` ;
222+ function buildKittyFocusCommand ( target , options = { } ) {
223+ return commandShape ( [ '@' , 'focus-window' , '--match' , targetMatch ( target ) ] , options ) ;
224+ }
225+
226+ function buildKittyCloseCommand ( target , options = { } ) {
227+ return commandShape ( [ '@' , 'close-window' , '--match' , targetMatch ( target ) ] , options ) ;
228+ }
229+
230+ function buildKittySendTextCommand ( target , options = { } ) {
231+ const shape = commandShape ( [ '@' , 'send-text' , '--match' , targetMatch ( target ) , '--stdin' ] , options ) ;
232+ if ( Object . prototype . hasOwnProperty . call ( options , 'input' ) || Object . prototype . hasOwnProperty . call ( options , 'text' ) || options . submit ) {
233+ shape . input = sendTextInput (
234+ Object . prototype . hasOwnProperty . call ( options , 'input' ) ? options . input : options . text ,
235+ { submit : options . submit } ,
236+ ) ;
237+ }
238+ return shape ;
124239}
125240
126241function buildFocusPaneCommand ( target , config = { } ) {
127- return commandShape ( [ '@' , 'focus-window' , '--match' , targetMatch ( target ) ] , config ) ;
242+ return buildKittyFocusCommand ( target , { kittyBin : kittyBin ( config , { allowEnv : true } ) } ) ;
128243}
129244
130245function buildClosePaneCommand ( target , config = { } ) {
131- return commandShape ( [ '@' , 'close-window' , '--match' , targetMatch ( target ) ] , config ) ;
246+ return buildKittyCloseCommand ( target , { kittyBin : kittyBin ( config , { allowEnv : true } ) } ) ;
132247}
133248
134249function buildSendTextCommand ( target , config = { } ) {
135- return commandShape ( [ '@' , 'send-text' , '--match' , targetMatch ( target ) , '--stdin' ] , config ) ;
250+ return buildKittySendTextCommand ( target , { kittyBin : kittyBin ( config , { allowEnv : true } ) } ) ;
136251}
137252
138253function sendTextInput ( value , options = { } ) {
@@ -200,6 +315,12 @@ function createBackend(config = {}) {
200315
201316module . exports = {
202317 DEFAULT_KITTY_BIN ,
318+ buildKittyLaunchCommand,
319+ buildKittyFocusCommand,
320+ buildKittyCloseCommand,
321+ buildKittySendTextCommand,
322+ buildKittyLsCommand,
323+ buildKittyVersionCommand,
203324 buildAvailabilityCommand,
204325 buildOpenCockpitLayoutCommand,
205326 buildLaunchAgentPaneCommand,
0 commit comments