2026-06-26 19:28:44 +03:00
import { EXIT_CODE , TunnelCliError } from './cli-errors.js' ;
import { getStartupStatus , enableStartupService , disableStartupService } from './cli-startup.js' ;
import {
intro as clackIntro ,
outro as clackOutro ,
isJsonMode ,
isQuietMode ,
printJson ,
logStatus ,
} from '../cli-output.js' ;
2026-09-05 09:12:43 -06:00
async function startupCommand ( options , action = 'status' , dependencies = {}) {
const getStatus = dependencies . getStartupStatus || getStartupStatus ;
const enableService = dependencies . enableStartupService || enableStartupService ;
const disableService = dependencies . disableStartupService || disableStartupService ;
2026-06-26 19:28:44 +03:00
const normalized = typeof action === 'string' ? action . trim (). toLowerCase () : 'status' ;
if ( ! [ 'status' , 'enable' , 'disable' ]. includes ( normalized )) {
throw new TunnelCliError (
`Unknown startup subcommand ' ${ action } '. Use 'openchamber startup --help'.` ,
EXIT_CODE . USAGE_ERROR
);
}
let status ;
if ( normalized === 'enable' ) {
2026-09-05 09:12:43 -06:00
status = enableService ( options );
2026-06-26 19:28:44 +03:00
} else if ( normalized === 'disable' ) {
2026-09-05 09:12:43 -06:00
status = disableService ();
2026-06-26 19:28:44 +03:00
} else {
2026-09-05 09:12:43 -06:00
status = getStatus ();
2026-06-26 19:28:44 +03:00
}
2026-09-05 09:12:43 -06:00
let result = { action : normalized , ... status };
2026-06-26 19:28:44 +03:00
if ( ! result . supported ) {
throw new TunnelCliError (
`Startup integration is not supported on ${ result . platform } .` ,
EXIT_CODE . USAGE_ERROR
);
}
if ( normalized === 'enable' && result . activeState === 'failed' ) {
throw new TunnelCliError (
'Startup service was installed but failed to start. Run `journalctl --user -u openchamber.service -n 80 --no-pager` for details.' ,
EXIT_CODE . GENERAL_ERROR
);
}
2026-09-05 09:12:43 -06:00
if ( result . platform === 'linux' && result . enabled && result . lingerEnabled !== true ) {
const user = result . lingerUser || '"$USER"' ;
const disabled = result . lingerEnabled === false ;
result = {
... result ,
messages : [{
level : 'warning' ,
code : disabled ? 'LINGER_DISABLED' : 'LINGER_UNKNOWN' ,
message : disabled
? `User lingering is disabled; the startup service may stop after logout. Run \`sudo loginctl enable-linger ${ user } \` to keep it running.`
: `Could not verify user lingering; the startup service may stop after logout. Check with \`loginctl show-user ${ user } -p Linger\`.` ,
}],
};
}
2026-06-26 19:28:44 +03:00
if ( isJsonMode ( options )) {
printJson ( result );
return ;
}
if ( isQuietMode ( options )) {
2026-09-05 09:12:43 -06:00
const lingerToken = result . platform === 'linux'
? ` linger: ${ result . lingerEnabled === true ? 'yes' : result . lingerEnabled === false ? 'no' : 'unknown' } `
: '' ;
process . stdout . write ( `startup ${ result . enabled ? 'enabled' : 'disabled' } platform: ${ result . platform } supported: ${ result . supported ? 'yes' : 'no' }${ result . servicePath ? ` path: ${ result . servicePath } ` : '' }${ lingerToken } \n` );
2026-06-26 19:28:44 +03:00
return ;
}
clackIntro ( 'OpenChamber Startup' );
logStatus ( result . enabled ? 'success' : 'info' , `startup ${ result . enabled ? 'enabled' : 'disabled' } ` , result . servicePath || undefined );
if ( typeof result . activeState === 'string' ) {
logStatus ( result . active ? 'success' : result . activeState === 'failed' ? 'error' : 'warning' , `service ${ result . activeState } ` );
}
if ( normalized === 'enable' ) {
logStatus ( 'info' , 'service command' , 'openchamber serve --foreground' );
}
2026-09-05 09:12:43 -06:00
if ( result . platform === 'linux' ) {
if ( result . lingerEnabled === true ) {
logStatus ( 'success' , 'user lingering enabled' );
} else if ( result . lingerEnabled === false ) {
logStatus ( result . enabled ? 'warning' : 'info' , ` ${ result . enabled ? '[LINGER_DISABLED] ' : '' } user lingering is disabled` , result . enabled ? 'startup service may stop after logout' : undefined );
if ( result . enabled ) {
logStatus ( 'info' , '[ENABLE_LINGER]' , `sudo loginctl enable-linger ${ result . lingerUser || '"$USER"' } ` );
}
} else {
logStatus ( result . enabled ? 'warning' : 'info' , result . enabled ? '[LINGER_UNKNOWN] could not verify user lingering' : 'user lingering state is unknown' , result . enabled ? 'startup service may stop after logout' : undefined );
if ( result . enabled ) {
logStatus ( 'info' , '[CHECK_LINGER]' , `loginctl show-user ${ result . lingerUser || '"$USER"' } -p Linger` );
}
}
}
2026-06-26 19:28:44 +03:00
clackOutro ( normalized === 'status' ? 'status complete' : ` ${ normalized } complete` );
}
export { startupCommand };