@@ -11,6 +11,7 @@ import {
1111import { loadTuiConfig } from '#/tui/config' ;
1212import { resolveCommandPath } from '#/utils/process/resolve-command' ;
1313
14+ import { resolveForkUpdateScript } from './fork-updater' ;
1415import { readUpdateCache } from './cache' ;
1516import { tryAcquireUpdateInstallLock } from './install-lock' ;
1617import { emptyUpdateInstallState , readUpdateInstallState , writeUpdateInstallState } from './install-state' ;
@@ -70,6 +71,12 @@ export function installCommandFor(
7071 version : string ,
7172 platform : NodeJS . Platform ,
7273) : string {
74+ if ( source === 'native' ) {
75+ const forkScript = resolveForkUpdateScript ( ) ;
76+ if ( forkScript !== undefined ) {
77+ return `bash ${ forkScript } --fast` ;
78+ }
79+ }
7380 switch ( source ) {
7481 case 'npm-global' :
7582 return `npm install -g ${ NPM_PACKAGE_NAME } @${ version } ` ;
@@ -128,13 +135,18 @@ export function spawnForSource(
128135 return { cmd : bunCommand ( platform ) , args : [ 'add' , '-g' , `${ NPM_PACKAGE_NAME } @${ version } ` ] } ;
129136 case 'homebrew' :
130137 return { cmd : 'brew' , args : [ 'upgrade' , 'kimi-code' ] } ;
131- case 'native' :
138+ case 'native' : {
139+ const forkScript = resolveForkUpdateScript ( ) ;
140+ if ( forkScript !== undefined ) {
141+ return { cmd : 'bash' , args : [ forkScript , '--auto' ] } ;
142+ }
132143 // Native installs self-spawn the hidden downloader sub-command, which
133144 // stages the binary next to the exe (verified against the release
134145 // manifest's sha256); the swap happens on the next startup. This
135146 // replaces the old `curl|bash` / `irm|iex` re-install dance — no shell,
136147 // no pipeline exit-status loss, no PowerShell dependency on Windows.
137148 return { cmd : process . execPath , args : [ '__update_download' , version ] } ;
149+ }
138150 case 'unsupported' :
139151 throw new Error ( 'unsupported install source cannot be auto-installed' ) ;
140152 }
@@ -173,12 +185,21 @@ function resolveInstallSpawn(
173185 platform : NodeJS . Platform ,
174186 options ?: { readonly manual ?: boolean } ,
175187) : { readonly resolvedCmd : string ; readonly args : readonly string [ ] ; readonly shell : boolean } | undefined {
176- const { cmd, args } = spawnForSource ( source , version , platform ) ;
177188 if ( source === 'native' ) {
189+ const forkScript = resolveForkUpdateScript ( ) ;
190+ if ( forkScript !== undefined ) {
191+ const flag = options ?. manual === true ? '--fast' : '--auto' ;
192+ return { resolvedCmd : 'bash' , args : [ forkScript , flag ] , shell : false } ;
193+ }
178194 // A user-confirmed install marks the stage as manual so the startup swap
179195 // applies it even when automatic updates are opted out via env.
180- return { resolvedCmd : cmd , args : options ?. manual === true ? [ ...args , '--manual' ] : args , shell : false } ;
196+ return {
197+ resolvedCmd : process . execPath ,
198+ args : options ?. manual === true ? [ '__update_download' , version , '--manual' ] : [ '__update_download' , version ] ,
199+ shell : false ,
200+ } ;
181201 }
202+ const { cmd, args } = spawnForSource ( source , version , platform ) ;
182203 const resolvedCmd = resolveSpawnCommand ( cmd , platform ) ;
183204 if ( resolvedCmd === undefined ) return undefined ;
184205 return { resolvedCmd, args, shell : platform === 'win32' } ;
@@ -209,9 +230,11 @@ export function renderManualUpdateMessage(
209230 case 'homebrew' :
210231 sourceDesc = 'homebrew' ;
211232 break ;
212- case 'native' :
213- sourceDesc = 'native installer' ;
233+ case 'native' : {
234+ const forkScript = resolveForkUpdateScript ( ) ;
235+ sourceDesc = forkScript !== undefined ? `fork orchestrator (${ forkScript } )` : 'native installer' ;
214236 break ;
237+ }
215238 case 'unsupported' :
216239 sourceDesc = 'unsupported package manager or layout.' ;
217240 break ;
0 commit comments