@@ -2,16 +2,19 @@ import { execSync } from "child_process";
22import { writeFileSync } from "fs" ;
33import { join } from "path" ;
44import {
5+ BailianError ,
6+ DEFAULT_INSTALL_PS1_URL ,
57 DEFAULT_INSTALL_SCRIPT_URL ,
68 defineCommand ,
79 getConfigDir ,
8- getInstallMethod ,
10+ getUpdateInstallMethod ,
911 type InstallMethod ,
1012} from "bailian-cli-core" ;
1113import {
1214 ansi ,
1315 fetchLatestVersion ,
1416 fetchBinaryChannelVersion ,
17+ normalizeBinaryVersion ,
1518 performBinaryUpdate ,
1619 type AnsiStyles ,
1720} from "bailian-cli-runtime" ;
@@ -50,64 +53,108 @@ async function resolveLatest(method: InstallMethod, npmPackage: string): Promise
5053 return fetchLatestVersion ( 5000 , npmPackage ) ;
5154}
5255
56+ function binaryReinstallHint ( ) : string {
57+ if ( process . platform === "win32" ) {
58+ return ` irm ${ DEFAULT_INSTALL_PS1_URL } | iex\n` ;
59+ }
60+ return ` curl -fsSL ${ DEFAULT_INSTALL_SCRIPT_URL } | bash\n` ;
61+ }
62+
5363export default defineCommand ( {
54- description : "Update the CLI to the latest version" ,
64+ description : "Update the CLI to the latest or a specified version" ,
5565 auth : "none" ,
56- exampleArgs : [ "" ] ,
66+ usageArgs : "[--to <version>]" ,
67+ flags : {
68+ to : {
69+ type : "string" ,
70+ valueHint : "<version>" ,
71+ description : "Install this exact version instead of the latest" ,
72+ } ,
73+ } ,
74+ exampleArgs : [ "" , "--to 0.1.14" ] ,
75+ validate ( flags ) {
76+ if ( flags . to !== undefined && ! flags . to . trim ( ) ) {
77+ return "--to requires a non-empty version" ;
78+ }
79+ return undefined ;
80+ } ,
5781 async run ( ctx ) {
5882 const { identity } = ctx ;
5983 const npmPackage = identity . npmPackage ;
6084 const binName = identity . binName ;
6185 const currentVersion = identity . version ;
6286 const color = ansi ( process . stderr ) ;
63- const method = getInstallMethod ( ) ;
87+ const method = getUpdateInstallMethod ( identity ) ;
88+ const requestedTo = ctx . flags . to ?. trim ( ) ;
89+ const pinnedVersion = requestedTo ? normalizeBinaryVersion ( requestedTo ) : undefined ;
6490
6591 process . stderr . write ( `Current version: ${ color . yellow ( currentVersion ) } \n` ) ;
6692 process . stderr . write ( `Install method: ${ color . dim ( method ) } \n` ) ;
67- process . stderr . write ( "Checking for updates...\n" ) ;
93+ if ( pinnedVersion ) {
94+ process . stderr . write ( `Target version: ${ color . green ( pinnedVersion ) } \n` ) ;
95+ } else {
96+ process . stderr . write ( "Checking for updates...\n" ) ;
97+ }
6898
6999 if ( method === "brew" || method === "winget" ) {
70100 const cmd =
71101 method === "brew" ? "brew upgrade bailian-cli" : "winget upgrade Aliyun.BailianCLI" ;
72102 process . stderr . write (
73103 `${ color . yellow ( `This CLI was installed via ${ method } . Update with:` ) } \n ${ cmd } \n` ,
74104 ) ;
105+ if ( pinnedVersion ) {
106+ process . stderr . write (
107+ `${ color . dim ( `Note: --to is not supported for ${ method } installs.` ) } \n` ,
108+ ) ;
109+ }
75110 return ;
76111 }
77112
78- const latest = await resolveLatest ( method , npmPackage ) ;
113+ const targetVersion = pinnedVersion ?? ( await resolveLatest ( method , npmPackage ) ) ;
114+
115+ if ( ! targetVersion ) {
116+ process . stderr . write ( `${ color . yellow ( "Could not determine the latest version." ) } \n` ) ;
117+ return ;
118+ }
79119
80- if ( latest && latest === currentVersion ) {
81- process . stderr . write ( `${ color . green ( `\u2713 Already up to date (${ currentVersion } ).` ) } \n` ) ;
120+ if ( targetVersion === currentVersion ) {
121+ const message = pinnedVersion
122+ ? `\u2713 Already at ${ currentVersion } .`
123+ : `\u2713 Already up to date (${ currentVersion } ).` ;
124+ process . stderr . write ( `${ color . green ( message ) } \n` ) ;
82125 if ( method === "npm" ) updateAgentSkill ( color ) ;
83126 return ;
84127 }
85128
86- if ( latest ) {
87- process . stderr . write ( `Latest version: ${ color . green ( latest ) } \n\n` ) ;
129+ if ( ! pinnedVersion ) {
130+ process . stderr . write ( `Latest version: ${ color . green ( targetVersion ) } \n\n` ) ;
88131 } else {
89- process . stderr . write ( `${ color . yellow ( "Could not determine the latest version." ) } \n` ) ;
90- return ;
132+ process . stderr . write ( "\n" ) ;
91133 }
92134
93135 if ( method === "binary" ) {
94136 process . stderr . write ( `Updating via binary channel...\n\n` ) ;
95137 try {
96- const newVer = await performBinaryUpdate ( latest ) ;
138+ const newVer = await performBinaryUpdate ( targetVersion ) ;
97139 process . stderr . write (
98140 `\n${ color . green ( `\u2713 Update complete: ${ currentVersion } \u2192 ${ newVer } ` ) } \n` ,
99141 ) ;
100142 writeUpdateState ( newVer ) ;
101143 } catch ( error ) {
102144 const message = error instanceof Error ? error . message : String ( error ) ;
145+ const reinstall =
146+ error instanceof BailianError && error . hint
147+ ? error . hint . replace ( / ^ R e - r u n : \s * / i, "" )
148+ : binaryReinstallHint ( ) . trim ( ) ;
103149 process . stderr . write ( `\nAutomatic binary update failed: ${ message } \n` ) ;
104150 process . stderr . write ( "Re-run the install script:\n" ) ;
105- process . stderr . write ( ` curl -fsSL ${ DEFAULT_INSTALL_SCRIPT_URL } | bash \n\n` ) ;
151+ process . stderr . write ( ` ${ reinstall } \n\n` ) ;
106152 }
107153 return ;
108154 }
109155
110- const cmd = `npm install -g ${ npmPackage } @latest` ;
156+ const npmSpec = pinnedVersion ? `${ npmPackage } @${ pinnedVersion } ` : `${ npmPackage } @latest` ;
157+ const cmd = `npm install -g ${ npmSpec } ` ;
111158 process . stderr . write ( `Updating ${ npmPackage } via npm...\n\n` ) ;
112159
113160 try {
0 commit comments