11import { platform } from "os" ;
2- import { defineCommand , detectOutputFormat , maskToken , type FlagsDef } from "bailian-cli-core" ;
2+ import {
3+ defineCommand ,
4+ detectOutputFormat ,
5+ maskToken ,
6+ BailianError ,
7+ ExitCode ,
8+ type FlagsDef ,
9+ } from "bailian-cli-core" ;
310import { emitResult , emitBare } from "bailian-cli-runtime" ;
411import { AGENTS , VALID_AGENT_NAMES , type WriteParams } from "./writers.ts" ;
512import { decodeTokenPlanKey } from "./decode-key.ts" ;
6- import { resolveRegionBaseUrl } from "./writers/utils.ts" ;
13+ import {
14+ resolveRegionBaseUrl ,
15+ findLatestBackup ,
16+ restoreLatestBackup ,
17+ } from "./writers/utils.ts" ;
718
819const FLAGS = {
920 agent : {
@@ -39,12 +50,12 @@ const FLAGS = {
3950 type : "string" ,
4051 valueHint : "<model>" ,
4152 description : "Default model name" ,
42- required : true ,
4353 } ,
4454 contextWindow : {
4555 type : "number" ,
4656 valueHint : "<tokens>" ,
47- description : "OpenClaw only: model context window in tokens (default: 256000)" ,
57+ description :
58+ "OpenClaw only: model context window in tokens (default: 256000)" ,
4859 } ,
4960 wireApi : {
5061 type : "string" ,
@@ -53,38 +64,106 @@ const FLAGS = {
5364 'Codex only: wire protocol (default: responses). "chat" only works with legacy Codex <= 0.80.0' ,
5465 choices : [ "chat" , "responses" ] ,
5566 } ,
67+ restore : {
68+ type : "switch" ,
69+ description :
70+ "Restore the agent's config files from the latest .bak backup created by this command" ,
71+ } ,
5672} satisfies FlagsDef ;
5773
5874export default defineCommand ( {
5975 description : "Configure a coding agent to use DashScope API" ,
6076 auth : "none" ,
6177 usageArgs :
62- "--agent <name> (--base-url <url> | --region <region>) (--api-key <key> | --key <encoded>) --model <model>" ,
78+ "--agent <name> (( --base-url <url> | --region <region>) (--api-key <key> | --key <encoded>) --model <model> | --restore) " ,
6379 flags : FLAGS ,
6480 exampleArgs : [
6581 "--agent claude-code --base-url https://dashscope.aliyuncs.com/apps/anthropic --api-key sk-xxxxx --model qwen3-max" ,
6682 "--agent qwen-code --base-url https://dashscope.aliyuncs.com/compatible-mode/v1 --api-key sk-xxxxx --model qwen3-coder-plus" ,
6783 "--agent codex --base-url https://dashscope.aliyuncs.com/compatible-mode/v1 --api-key sk-xxxxx --model qwen3-coder-plus" ,
84+ "--agent claude-code --restore" ,
6885 ] ,
6986 validate ( flags ) {
70- if ( ! flags . baseUrl && ! flags . region ) return "one of --base-url or --region is required" ;
71- if ( flags . baseUrl && flags . region ) return "--base-url and --region are mutually exclusive" ;
72- if ( ! flags . apiKey && ! flags . key ) return "one of --api-key or --key is required" ;
73- if ( flags . apiKey && flags . key ) return "--api-key and --key are mutually exclusive" ;
87+ if ( flags . restore ) {
88+ const writeFlags = [
89+ flags . baseUrl ,
90+ flags . region ,
91+ flags . apiKey ,
92+ flags . key ,
93+ flags . model ,
94+ ] ;
95+ if ( writeFlags . some ( ( value ) => value !== undefined ) ) {
96+ return "--restore cannot be combined with --base-url/--region/--api-key/--key/--model" ;
97+ }
98+ return undefined ;
99+ }
100+ if ( ! flags . model ) return "--model is required" ;
101+ if ( ! flags . baseUrl && ! flags . region )
102+ return "one of --base-url or --region is required" ;
103+ if ( flags . baseUrl && flags . region )
104+ return "--base-url and --region are mutually exclusive" ;
105+ if ( ! flags . apiKey && ! flags . key )
106+ return "one of --api-key or --key is required" ;
107+ if ( flags . apiKey && flags . key )
108+ return "--api-key and --key are mutually exclusive" ;
74109 return undefined ;
75110 } ,
76111 async run ( ctx ) {
77112 const { settings, flags } = ctx ;
78113 const agentName = flags . agent ;
79- const { model, contextWindow, wireApi } = flags ;
114+ const { contextWindow, wireApi } = flags ;
115+ const agentDef = AGENTS [ agentName ] ;
116+ const format = detectOutputFormat ( settings . output ) ;
117+
118+ // --restore: roll each managed config file back to its newest .bak sibling.
119+ if ( flags . restore ) {
120+ const paths = agentDef . configPaths ( ) ;
121+
122+ if ( settings . dryRun ) {
123+ emitResult (
124+ {
125+ agent : agentName ,
126+ label : agentDef . label ,
127+ restore : paths . map ( ( path ) => ( {
128+ path,
129+ backup : findLatestBackup ( path ) ?? null ,
130+ } ) ) ,
131+ } ,
132+ format ,
133+ ) ;
134+ return ;
135+ }
136+
137+ const results = paths . map ( ( path ) => restoreLatestBackup ( path ) ) ;
138+ if ( results . every ( ( result ) => ! result . backupPath ) ) {
139+ throw new BailianError (
140+ `No backups found for ${ agentDef . label } .` ,
141+ ExitCode . GENERAL ,
142+ "Backups are created as <config>.bak.<timestamp> next to each config file when this command writes it." ,
143+ ) ;
144+ }
145+
146+ if ( ! settings . quiet ) {
147+ emitBare ( `${ agentDef . label } config restored from backup.` ) ;
148+ for ( const result of results ) {
149+ if ( result . backupPath )
150+ emitBare ( ` Restored: ${ result . path } <- ${ result . backupPath } ` ) ;
151+ else emitBare ( ` Skipped (no backup): ${ result . path } ` ) ;
152+ }
153+ }
154+ return ;
155+ }
156+
157+ // Write mode — validate() guarantees these flags are present.
158+ const model = flags . model ! ;
80159 // --region is a Token Plan convenience: convert it into a base URL and use
81160 // it exactly as --base-url would be.
82- const baseUrl = flags . region ? resolveRegionBaseUrl ( flags . region ) : flags . baseUrl ! ;
161+ const baseUrl = flags . region
162+ ? resolveRegionBaseUrl ( flags . region )
163+ : flags . baseUrl ! ;
83164 // --key carries the web console's obfuscated form; decode it up front so
84165 // even --dry-run validates the token.
85166 const apiKey = flags . key ? decodeTokenPlanKey ( flags . key ) : flags . apiKey ! ;
86- const agentDef = AGENTS [ agentName ] ;
87- const format = detectOutputFormat ( settings . output ) ;
88167
89168 // Hermes has no native Windows support.
90169 if ( agentName === "hermes" && platform ( ) === "win32" ) {
0 commit comments