@@ -19,6 +19,7 @@ import {
1919 logger ,
2020 AuthenticationError ,
2121 configureLoggingPreset ,
22+ getActiveLogPaths ,
2223} from "appwrite-utils-helpers" ;
2324import { ConfirmationDialogs } from "./shared/confirmationDialogs.js" ;
2425import { SelectionDialogs } from "./shared/selectionDialogs.js" ;
@@ -99,6 +100,7 @@ interface CliOptions {
99100 useSession ? : boolean ;
100101 sessionCookie ? : string ;
101102 debug ? : boolean ;
103+ logDir ? : string ;
102104 listBackups ? : boolean ;
103105 autoSync ? : boolean ;
104106 selectBuckets ? : boolean ;
@@ -497,7 +499,7 @@ const argv = yargs(hideBin(process.argv))
497499 . option ( "autoSync" , {
498500 alias : [ "auto" ] ,
499501 type : "boolean" ,
500- description : "Skip prompts and sync all databases, tables, and buckets (current behavior) "
502+ description : "Skip every interactive prompt for --push and -- sync: select all databases, all local tables, no confirmation. The flag CI workflows want. "
501503 } )
502504 . option ( "selectBuckets" , {
503505 type : "boolean" ,
@@ -839,6 +841,11 @@ const argv = yargs(hideBin(process.argv))
839841 default : false ,
840842 description : "Enable verbose helpers logging (debug level + console transport). Use when auth/discovery is failing silently." ,
841843 } )
844+ . option ( "logDir" , {
845+ alias : [ "log-dir" ] ,
846+ type : "string" ,
847+ description : "Override the directory where the CLI writes combined.log and error.log. Defaults to ~/.appwrite-utils-cli/logs/." ,
848+ } )
842849 . parse ( ) as ParsedArgv ;
843850
844851// Idempotent process-wide exit. Multiple SIGINTs (or SIGINT-then-SIGTERM)
@@ -854,6 +861,15 @@ function __awuExit(code: number): void {
854861process . on ( "SIGINT" , ( ) => __awuExit ( 130 ) ) ;
855862process . on ( "SIGTERM" , ( ) => __awuExit ( 143 ) ) ;
856863
864+ // Tell the user where the centralized log file lives, both at startup and on
865+ // failure. Without this they hunt for it; with it they can paste one path.
866+ function __awuPrintLogPath ( ) : void {
867+ const paths = getActiveLogPaths ( ) ;
868+ if ( ! paths ) return ;
869+ // eslint-disable-next-line no-console
870+ console . error ( `📝 Full log: ${ paths . combined } ` ) ;
871+ }
872+
857873// Global error capture. Without these handlers, an inquirer-internal crash
858874// (or any other top-level throw inside an async path) just dumps a raw stack
859875// trace and dies — making bug reports impossible to triage because the
@@ -866,6 +882,7 @@ process.on("uncaughtException", (error) => {
866882 error,
867883 context : { cwd : process . cwd ( ) } ,
868884 } ) ;
885+ __awuPrintLogPath ( ) ;
869886 __awuExit ( 1 ) ;
870887} ) ;
871888process . on ( "unhandledRejection" , ( reason ) => {
@@ -875,20 +892,27 @@ process.on("unhandledRejection", (reason) => {
875892 error : reason ,
876893 context : { cwd : process . cwd ( ) } ,
877894 } ) ;
895+ __awuPrintLogPath ( ) ;
878896 __awuExit ( 1 ) ;
879897} ) ;
880898
881899async function main ( ) {
882900 const startTime = Date . now ( ) ;
883901 const operationStats : Record < string , number > = { } ;
884902
885- // --debug: flip the helpers winston logger from silent default to
886- // debug-level with console transport. Without this, all the diagnostic
887- // logs in SessionAuthService/ConfigManager are invisible — the silent
888- // default is great for production but useless when something's broken .
903+ // Centralized logging: write everything to a stable, predictable file path
904+ // by default so users can always paste a single log. --debug additionally
905+ // turns on the console transport at debug level. --logDir overrides the
906+ // directory both presets write to .
889907 if ( argv . debug ) {
890- configureLoggingPreset ( "debug" ) ;
908+ configureLoggingPreset ( "debug" , argv . logDir ) ;
891909 MessageFormatter . info ( "Debug logging enabled (helpers logs → console at debug level)" , { prefix : "CLI" } ) ;
910+ } else {
911+ configureLoggingPreset ( "file" , argv . logDir ) ;
912+ }
913+ const __logPaths = getActiveLogPaths ( ) ;
914+ if ( __logPaths ) {
915+ MessageFormatter . info ( `Logs → ${ __logPaths . combined } ` , { prefix : "CLI" } ) ;
892916 }
893917
894918 if ( ( argv . schemaFormat || argv . schemaOutDir ) && ! argv . generate ) {
@@ -1645,6 +1669,13 @@ async function main() {
16451669 let selectedDbIds : string [ ] = [ ] ;
16461670 if ( parsedArgv . dbIds ) {
16471671 selectedDbIds = parsedArgv . dbIds . split ( / [ , \s ] + / ) . filter ( Boolean ) ;
1672+ } else if ( parsedArgv . autoSync ) {
1673+ // --auto: skip the interactive picker and push every available DB.
1674+ selectedDbIds = availableDatabases . map ( db => db . $id ) ;
1675+ MessageFormatter . info (
1676+ `--auto: selected all ${ selectedDbIds . length } database(s): ${ selectedDbIds . join ( ", " ) } ` ,
1677+ { prefix : "Push" }
1678+ ) ;
16481679 } else {
16491680 selectedDbIds = await SelectionDialogs . selectDatabases (
16501681 availableDatabases ,
@@ -1704,6 +1735,9 @@ async function main() {
17041735 if ( parsedArgv . tableIds ) {
17051736 // Non-interactive: respect provided table IDs as-is (apply to each selected DB)
17061737 selectedTableIds = parsedArgv . tableIds . split ( / [ \, \s ] + / ) . filter ( Boolean ) ;
1738+ } else if ( parsedArgv . autoSync ) {
1739+ // --auto: take every local item for this DB, no prompt.
1740+ selectedTableIds = [ ...localItemIds ] ;
17071741 } else {
17081742 const inquirer = ( await import ( "inquirer" ) ) . default ;
17091743 const choices : Array < { name : string ; value: string } > = [ ] ;
@@ -1788,8 +1822,9 @@ async function main() {
17881822 collections : databaseSelections . reduce ( ( sum , s ) => sum + s . tableIds . length , 0 ) ,
17891823 details : databaseSelections . map ( s => `${ s . databaseId } : ${ s . tableIds . length } items` ) ,
17901824 } ;
1791- // Skip confirmation if both dbIds and tableIds are provided (non-interactive)
1792- if ( ! ( parsedArgv . dbIds && parsedArgv . tableIds ) ) {
1825+ // Skip confirmation when running non-interactively: either --auto, or
1826+ // both --dbIds and --tableIds explicitly provided.
1827+ if ( ! parsedArgv . autoSync && ! ( parsedArgv . dbIds && parsedArgv . tableIds ) ) {
17931828 const confirmed = await ConfirmationDialogs . showOperationSummary ( 'Push' , pushSummary , { confirmationRequired : true } ) ;
17941829 if ( ! confirmed ) {
17951830 MessageFormatter . info ( "Push operation cancelled" , { prefix : "Push" } ) ;
@@ -1963,5 +1998,6 @@ main().catch((error) => {
19631998 return ;
19641999 }
19652000 MessageFormatter . error ( "CLI execution failed" , error , { prefix : "CLI" } ) ;
2001+ __awuPrintLogPath ( ) ;
19662002 process . exit ( 1 ) ;
19672003} ) ;
0 commit comments