Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/cli/src/commands/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import { buildProviderKeyName, SecretsManager } from '@tinyclaw/secrets';
import type { StreamCallback } from '@tinyclaw/types';
import { createWebUI } from '@tinyclaw/web';
import QRCode from 'qrcode';
import { isRunningInContainer } from '../detect-container.js';
import { showBanner } from '../ui/banner.js';
import { theme } from '../ui/theme.js';

Expand Down Expand Up @@ -210,6 +211,34 @@ export async function setupCommand(): Promise<void> {

p.intro(theme.brand("Let's set up Tiny Claw"));

// --- Container environment warning ----------------------------------

if (isRunningInContainer()) {
p.note(
theme.warn('Container Environment Detected') +
'\n\n' +
'Interactive CLI setup may not work properly in Docker/containers.\n' +
'If prompts freeze or fail, cancel and run:\n\n' +
' ' +
theme.cmd('tinyclaw setup --docker') +
'\n\n' +
'Or use ' +
theme.cmd('--web') +
' for browser-based setup.',
'Docker/Container',
);

const continueAnyway = await p.confirm({
message: 'Continue with interactive setup anyway?',
initialValue: false,
});

if (p.isCancel(continueAnyway) || !continueAnyway) {
p.outro(theme.dim('Setup cancelled. Use --docker or --web flag for container environments.'));
process.exit(0);
}
}

// --- Security warning -----------------------------------------------

p.note(
Expand Down
27 changes: 27 additions & 0 deletions src/cli/src/detect-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { existsSync, readFileSync } from 'node:fs';

/**
* Detect if running inside a Docker container or CI environment.
* Checks for common indicators: .dockerenv, cgroup, CI env vars, container-specific env vars.
*/
export function isRunningInContainer(): boolean {
if (process.env.CI || process.env.CONTAINER || process.env.DOCKER_CONTAINER) {
return true;
}
try {
if (existsSync('/.dockerenv')) {
return true;
}
} catch {
/* ignore */
}
try {
const cgroup = readFileSync('/proc/1/cgroup', 'utf8');
if (/docker|containerd|kubepods|lxc|podman/i.test(cgroup)) {
return true;
}
} catch {
/* ignore */
}
return false;
}
30 changes: 21 additions & 9 deletions src/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
* Lightweight argument router. No framework — just process.argv.
*
* Usage:
* tinyclaw Show banner + help
* tinyclaw setup Interactive first-time setup wizard
* tinyclaw setup --web Start web onboarding at /setup
* tinyclaw start Boot the agent (requires setup first)
* tinyclaw purge Wipe all data for a fresh install
* tinyclaw --version Print version
* tinyclaw --help Show help
* tinyclaw Show banner + help
* tinyclaw setup Interactive first-time setup wizard
* tinyclaw setup --web Start web onboarding at /setup
* tinyclaw setup --docker Force web mode for Docker/container environments
* tinyclaw start Boot the agent (requires setup first)
* tinyclaw purge Wipe all data for a fresh install
* tinyclaw --version Print version
* tinyclaw --help Show help
*/

import { logger } from '@tinyclaw/logger';
import { isRunningInContainer } from './detect-container.js';
import { getVersion, showBanner } from './ui/banner.js';
import { theme } from './ui/theme.js';

Expand All @@ -27,8 +29,10 @@ function showHelp(): void {
console.log(` ${theme.cmd('tinyclaw')} ${theme.dim('<command>')}`);
console.log();
console.log(` ${theme.label('Commands')}`);
console.log(` ${theme.cmd('setup')} Interactive setup wizard`);
console.log(` ${theme.dim('Use --web for browser onboarding')}`);
console.log(
` ${theme.cmd('setup')} Interactive setup wizard (use --web for browser onboarding)`,
` ${theme.dim('Use --docker for Docker/container environments (auto-detected)')}`,
);
console.log(` ${theme.cmd('start')} Start the Tiny Claw agent`);
console.log(` ${theme.cmd('config')} Manage models, providers, and settings`);
Expand All @@ -40,6 +44,10 @@ function showHelp(): void {
);
console.log();
console.log(` ${theme.label('Options')}`);
console.log(
` ${theme.dim('--docker')} Force web-based setup for Docker/container environments`,
);
console.log(` ${theme.dim('--web')} Use web-based setup instead of CLI wizard`);
console.log(` ${theme.dim('--verbose')} Show debug-level logs during start`);
console.log(` ${theme.dim('--version, -v')} Show version number`);
console.log(` ${theme.dim('--help, -h')} Show this help message`);
Expand All @@ -54,7 +62,11 @@ async function main(): Promise<void> {

switch (command) {
case 'setup': {
if (args.includes('--web')) {
// Detect Docker/container environment and auto-route to web mode
const isDocker = args.includes('--docker') || isRunningInContainer();
const isWeb = args.includes('--web') || isDocker;

if (isWeb) {
// Web setup goes through supervisor so the restart mechanism works
const { supervisedStart } = await import('./supervisor.js');
await supervisedStart();
Expand Down
Loading