Skip to content

Commit 70b1db9

Browse files
🔧 update (cli): add --docker flag for container environments
- Add --docker flag that routes to web-based setup - Auto-detect Docker/container environments via: - CI, CONTAINER, DOCKER_CONTAINER env vars - /.dockerenv file - /proc/1/cgroup container indicators - Add container detection warning in interactive setup - Update help text with --docker documentation Closes #12
1 parent 2453218 commit 70b1db9

2 files changed

Lines changed: 98 additions & 11 deletions

File tree

src/cli/src/commands/setup.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ import { createWebUI } from '@tinyclaw/web';
5353
import QRCode from 'qrcode';
5454
import { showBanner } from '../ui/banner.js';
5555
import { theme } from '../ui/theme.js';
56+
import { existsSync, readFileSync } from 'fs';
57+
58+
/**
59+
* Detect if running inside a Docker container or CI environment.
60+
*/
61+
function isRunningInContainer(): boolean {
62+
if (process.env.CI || process.env.CONTAINER || process.env.DOCKER_CONTAINER) {
63+
return true;
64+
}
65+
try {
66+
if (existsSync('/.dockerenv')) {
67+
return true;
68+
}
69+
} catch { /* ignore */ }
70+
try {
71+
const cgroup = readFileSync('/proc/1/cgroup', 'utf8');
72+
if (/docker|containerd|kubepods|lxc|podman/i.test(cgroup)) {
73+
return true;
74+
}
75+
} catch { /* ignore */ }
76+
return false;
77+
}
5678

5779
/**
5880
* Copy text to the system clipboard.
@@ -205,6 +227,29 @@ export async function setupCommand(): Promise<void> {
205227

206228
showBanner();
207229

230+
// --- Container environment warning ----------------------------------
231+
232+
if (isRunningInContainer()) {
233+
p.note(
234+
theme.warn('Container Environment Detected') + '\n\n' +
235+
'Interactive CLI setup may not work properly in Docker/containers.\n' +
236+
'If prompts freeze or fail, cancel and run:\n\n' +
237+
' ' + theme.cmd('tinyclaw setup --docker') + '\n\n' +
238+
'Or use ' + theme.cmd('--web') + ' for browser-based setup.',
239+
'Docker/Container'
240+
);
241+
242+
const continueAnyway = await p.confirm({
243+
message: 'Continue with interactive setup anyway?',
244+
initialValue: false,
245+
});
246+
247+
if (p.isCancel(continueAnyway) || !continueAnyway) {
248+
p.outro(theme.dim('Setup cancelled. Use --docker or --web flag for container environments.'));
249+
process.exit(0);
250+
}
251+
}
252+
208253
const secretsManager = await SecretsManager.create();
209254
const configManager = await ConfigManager.create();
210255

src/cli/src/index.ts

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,54 @@
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

1819
import { logger } from '@tinyclaw/logger';
1920
import { getVersion, showBanner } from './ui/banner.js';
2021
import { 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 (/docker|containerd|kubepods|lxc|podman/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

Comments
 (0)