66 * Lightweight argument router. No framework — just process.argv.
77 *
88 * Usage:
9- * tinyclaw Show banner + help
10- * tinyclaw setup Interactive first-time setup wizard
11- * tinyclaw setup --web Start web onboarding at /setup
12- * tinyclaw start Boot the agent (requires setup first)
13- * tinyclaw purge Wipe all data for a fresh install
14- * tinyclaw --version Print version
15- * tinyclaw --help Show help
9+ * tinyclaw Show banner + help
10+ * tinyclaw setup Interactive first-time setup wizard
11+ * tinyclaw setup --web Start web onboarding at /setup
12+ * tinyclaw setup --docker Force web mode for Docker/container environments
13+ * tinyclaw start Boot the agent (requires setup first)
14+ * tinyclaw purge Wipe all data for a fresh install
15+ * tinyclaw --version Print version
16+ * tinyclaw --help Show help
1617 */
1718
1819import { logger } from '@tinyclaw/logger' ;
1920import { getVersion , showBanner } from './ui/banner.js' ;
2021import { theme } from './ui/theme.js' ;
22+ import { existsSync , readFileSync } from 'fs' ;
23+
24+ // ── Docker Detection ───────────────────────────────────────────────────
25+
26+ /**
27+ * Detect if running inside a Docker container or CI environment.
28+ * Checks for common indicators: .dockerenv, cgroup, CI env vars, container-specific env vars.
29+ */
30+ function detectDockerEnvironment ( ) : boolean {
31+ // Check for explicit CI/container environment variables
32+ if ( process . env . CI || process . env . CONTAINER || process . env . DOCKER_CONTAINER ) {
33+ return true ;
34+ }
35+
36+ // Check for .dockerenv file (older Docker versions)
37+ try {
38+ if ( existsSync ( '/.dockerenv' ) ) {
39+ return true ;
40+ }
41+ } catch {
42+ // Ignore errors
43+ }
44+
45+ // Check cgroup for container indicators
46+ try {
47+ const cgroup = readFileSync ( '/proc/1/cgroup' , 'utf8' ) ;
48+ if ( / d o c k e r | c o n t a i n e r d | k u b e p o d s | l x c | p o d m a n / i. test ( cgroup ) ) {
49+ return true ;
50+ }
51+ } catch {
52+ // Ignore errors (file may not exist or be readable)
53+ }
54+
55+ return false ;
56+ }
2157
2258// ── Help text ──────────────────────────────────────────────────────────
2359
@@ -27,9 +63,9 @@ function showHelp(): void {
2763 console . log ( ` ${ theme . cmd ( 'tinyclaw' ) } ${ theme . dim ( '<command>' ) } ` ) ;
2864 console . log ( ) ;
2965 console . log ( ` ${ theme . label ( 'Commands' ) } ` ) ;
30- console . log (
31- ` ${ theme . cmd ( 'setup' ) } Interactive setup wizard (use --web for browser onboarding)` ,
32- ) ;
66+ console . log ( ` ${ theme . cmd ( 'setup' ) } Interactive setup wizard` ) ;
67+ console . log ( ` ${ theme . dim ( 'Use --web for browser onboarding' ) } ` ) ;
68+ console . log ( ` ${ theme . dim ( 'Use --docker for Docker/container environments (auto-detected)' ) } ` ) ;
3369 console . log ( ` ${ theme . cmd ( 'start' ) } Start the Tiny Claw agent` ) ;
3470 console . log ( ` ${ theme . cmd ( 'config' ) } Manage models, providers, and settings` ) ;
3571 console . log ( ` ${ theme . cmd ( 'seed' ) } Show your Tiny Claw's soul seed` ) ;
@@ -39,6 +75,8 @@ function showHelp(): void {
3975 ) ;
4076 console . log ( ) ;
4177 console . log ( ` ${ theme . label ( 'Options' ) } ` ) ;
78+ console . log ( ` ${ theme . dim ( '--docker' ) } Force web-based setup for Docker/container environments` ) ;
79+ console . log ( ` ${ theme . dim ( '--web' ) } Use web-based setup instead of CLI wizard` ) ;
4280 console . log ( ` ${ theme . dim ( '--verbose' ) } Show debug-level logs during start` ) ;
4381 console . log ( ` ${ theme . dim ( '--version, -v' ) } Show version number` ) ;
4482 console . log ( ` ${ theme . dim ( '--help, -h' ) } Show this help message` ) ;
@@ -53,7 +91,11 @@ async function main(): Promise<void> {
5391
5492 switch ( command ) {
5593 case 'setup' : {
56- if ( args . includes ( '--web' ) ) {
94+ // Detect Docker/container environment and auto-route to web mode
95+ const isDocker = args . includes ( '--docker' ) || detectDockerEnvironment ( ) ;
96+ const isWeb = args . includes ( '--web' ) || isDocker ;
97+
98+ if ( isWeb ) {
5799 // Web setup goes through supervisor so the restart mechanism works
58100 const { supervisedStart } = await import ( './supervisor.js' ) ;
59101 await supervisedStart ( ) ;
0 commit comments