claude-phone-local 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +108 -0
- package/Dockerfile +84 -0
- package/LICENSE +22 -0
- package/README.md +231 -0
- package/claude-api-server/package-lock.json +832 -0
- package/claude-api-server/package.json +20 -0
- package/claude-api-server/server.js +540 -0
- package/claude-api-server/structured.js +208 -0
- package/cli/README.md +231 -0
- package/cli/bin/check-publish-safety.js +53 -0
- package/cli/bin/claude-phone.js +23 -0
- package/cli/bin/cli-main.js +284 -0
- package/cli/bin/postinstall.js +20 -0
- package/cli/lib/commands/api-server.js +77 -0
- package/cli/lib/commands/backup.js +69 -0
- package/cli/lib/commands/config/path.js +30 -0
- package/cli/lib/commands/config/reset.js +73 -0
- package/cli/lib/commands/config/show.js +86 -0
- package/cli/lib/commands/device/add.js +143 -0
- package/cli/lib/commands/device/list.js +55 -0
- package/cli/lib/commands/device/remove.js +83 -0
- package/cli/lib/commands/doctor.js +422 -0
- package/cli/lib/commands/logs.js +186 -0
- package/cli/lib/commands/restore.js +172 -0
- package/cli/lib/commands/setup.js +1246 -0
- package/cli/lib/commands/start.js +346 -0
- package/cli/lib/commands/status.js +139 -0
- package/cli/lib/commands/stop.js +101 -0
- package/cli/lib/commands/uninstall.js +183 -0
- package/cli/lib/commands/update.js +205 -0
- package/cli/lib/config.js +113 -0
- package/cli/lib/docker.js +384 -0
- package/cli/lib/mcp-register.js +108 -0
- package/cli/lib/network.js +109 -0
- package/cli/lib/platform.js +85 -0
- package/cli/lib/port-check.js +135 -0
- package/cli/lib/prereqs/checks/compose.js +147 -0
- package/cli/lib/prereqs/checks/disk.js +89 -0
- package/cli/lib/prereqs/checks/docker.js +155 -0
- package/cli/lib/prereqs/checks/network.js +78 -0
- package/cli/lib/prereqs/checks/node.js +95 -0
- package/cli/lib/prereqs/installers/docker-desktop.js +248 -0
- package/cli/lib/prereqs/installers/docker.js +229 -0
- package/cli/lib/prereqs/installers/node.js +254 -0
- package/cli/lib/prereqs/platform.js +175 -0
- package/cli/lib/prereqs/utils/execute.js +208 -0
- package/cli/lib/prereqs/utils/rollback.js +223 -0
- package/cli/lib/prereqs/utils/sudo.js +177 -0
- package/cli/lib/prereqs.js +228 -0
- package/cli/lib/prerequisites.js +115 -0
- package/cli/lib/process-manager.js +176 -0
- package/cli/lib/utils.js +78 -0
- package/cli/lib/validators.js +219 -0
- package/cli/lib/voice-downloader.js +79 -0
- package/docker/entrypoint.sh +110 -0
- package/docker/supervisord.conf +78 -0
- package/docker-compose.yml +40 -0
- package/docs/CLAUDE-CODE-SKILL.md +45 -0
- package/docs/LANGUAGES.md +113 -0
- package/docs/MCP-SERVER.md +111 -0
- package/docs/TROUBLESHOOTING.md +288 -0
- package/mcp-server/index.js +159 -0
- package/mcp-server/package-lock.json +1201 -0
- package/mcp-server/package.json +9 -0
- package/package.json +73 -0
- package/stt-local/Dockerfile +12 -0
- package/stt-local/server.py +45 -0
- package/tts-local/Dockerfile +9 -0
- package/tts-local/server.py +83 -0
- package/voice-app/API-QUERY-CONTRACT.md +238 -0
- package/voice-app/DEPLOYMENT.md +176 -0
- package/voice-app/Dockerfile +20 -0
- package/voice-app/README-OUTBOUND.md +316 -0
- package/voice-app/config/devices.json.example +18 -0
- package/voice-app/index.js +273 -0
- package/voice-app/lib/audio-fork.js +465 -0
- package/voice-app/lib/claude-bridge.js +113 -0
- package/voice-app/lib/connection-retry.js +58 -0
- package/voice-app/lib/conversation-loop.js +490 -0
- package/voice-app/lib/device-registry.js +171 -0
- package/voice-app/lib/http-server.js +242 -0
- package/voice-app/lib/logger.js +35 -0
- package/voice-app/lib/multi-registrar.js +133 -0
- package/voice-app/lib/outbound-handler.js +299 -0
- package/voice-app/lib/outbound-routes.js +417 -0
- package/voice-app/lib/outbound-session.js +324 -0
- package/voice-app/lib/query-routes.js +484 -0
- package/voice-app/lib/registrar.js +144 -0
- package/voice-app/lib/sip-handler.js +490 -0
- package/voice-app/lib/tts-service.js +205 -0
- package/voice-app/lib/whisper-client.js +140 -0
- package/voice-app/package.json +38 -0
- package/voice-app/static/gotit-beep.wav +0 -0
- package/voice-app/static/hold-music.wav +0 -0
- package/voice-app/static/ready-beep.wav +0 -0
- package/voice-app/test/freeswitch-retry.test.js +100 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import { runWithLogging } from '../utils/execute.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Install or launch Docker Desktop on macOS
|
|
10
|
+
* @param {object} _platform - Platform info from detectPlatform()
|
|
11
|
+
* @returns {Promise<{success: boolean, cancelled?: boolean}>}
|
|
12
|
+
*/
|
|
13
|
+
export async function installDockerDesktop(_platform) {
|
|
14
|
+
console.log(chalk.bold.cyan('\n📦 Docker Desktop\n'));
|
|
15
|
+
|
|
16
|
+
// Check if Docker Desktop is installed
|
|
17
|
+
const installed = isDockerDesktopInstalled();
|
|
18
|
+
|
|
19
|
+
if (installed) {
|
|
20
|
+
// Docker Desktop is installed but not running
|
|
21
|
+
return await launchDockerDesktop();
|
|
22
|
+
} else {
|
|
23
|
+
// Docker Desktop not installed - offer to install
|
|
24
|
+
return await installDockerDesktopNew();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if Docker Desktop is installed
|
|
30
|
+
* @returns {boolean} True if installed
|
|
31
|
+
*/
|
|
32
|
+
function isDockerDesktopInstalled() {
|
|
33
|
+
return fs.existsSync('/Applications/Docker.app');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Launch existing Docker Desktop installation
|
|
38
|
+
* @returns {Promise<{success: boolean, cancelled?: boolean}>}
|
|
39
|
+
*/
|
|
40
|
+
async function launchDockerDesktop() {
|
|
41
|
+
console.log(chalk.yellow('Docker Desktop is installed but not running.\n'));
|
|
42
|
+
|
|
43
|
+
const { launch } = await inquirer.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'confirm',
|
|
46
|
+
name: 'launch',
|
|
47
|
+
message: 'Launch Docker Desktop?',
|
|
48
|
+
default: true
|
|
49
|
+
}
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
if (!launch) {
|
|
53
|
+
console.log(chalk.gray('\nPlease launch Docker Desktop manually and run "claude-phone setup" again.\n'));
|
|
54
|
+
return { success: false, cancelled: true };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Launch Docker Desktop
|
|
58
|
+
try {
|
|
59
|
+
console.log(chalk.cyan('\nLaunching Docker Desktop...'));
|
|
60
|
+
execSync('open -a Docker', { stdio: 'pipe' });
|
|
61
|
+
|
|
62
|
+
// Wait for Docker daemon to start
|
|
63
|
+
return await waitForDockerDaemon();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(chalk.red(`\n❌ Failed to launch Docker Desktop: ${error.message}`));
|
|
66
|
+
return { success: false };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Install Docker Desktop via Homebrew or manual download
|
|
72
|
+
* @returns {Promise<{success: boolean, cancelled?: boolean}>}
|
|
73
|
+
*/
|
|
74
|
+
async function installDockerDesktopNew() {
|
|
75
|
+
console.log(chalk.yellow('Docker Desktop is not installed.\n'));
|
|
76
|
+
|
|
77
|
+
// Check if Homebrew is available
|
|
78
|
+
const hasHomebrew = checkHomebrew();
|
|
79
|
+
|
|
80
|
+
if (hasHomebrew) {
|
|
81
|
+
// Offer Homebrew installation
|
|
82
|
+
const { method } = await inquirer.prompt([
|
|
83
|
+
{
|
|
84
|
+
type: 'list',
|
|
85
|
+
name: 'method',
|
|
86
|
+
message: 'How would you like to install Docker Desktop?',
|
|
87
|
+
choices: [
|
|
88
|
+
{ name: 'Install via Homebrew (recommended)', value: 'brew' },
|
|
89
|
+
{ name: 'Manual download from docker.com', value: 'manual' },
|
|
90
|
+
{ name: 'Cancel', value: 'cancel' }
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
if (method === 'cancel') {
|
|
96
|
+
return { success: false, cancelled: true };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (method === 'brew') {
|
|
100
|
+
return await installViaHomebrew();
|
|
101
|
+
} else {
|
|
102
|
+
return await installManually();
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// No Homebrew - only manual option
|
|
106
|
+
return await installManually();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Install Docker Desktop via Homebrew
|
|
112
|
+
* @returns {Promise<{success: boolean}>}
|
|
113
|
+
*/
|
|
114
|
+
async function installViaHomebrew() {
|
|
115
|
+
try {
|
|
116
|
+
console.log(chalk.cyan('\nInstalling Docker Desktop via Homebrew...'));
|
|
117
|
+
console.log(chalk.gray('This may take several minutes...\n'));
|
|
118
|
+
|
|
119
|
+
const result = await runWithLogging('brew install --cask docker');
|
|
120
|
+
|
|
121
|
+
if (!result.success) {
|
|
122
|
+
console.error(chalk.red('\n❌ Homebrew installation failed'));
|
|
123
|
+
return { success: false };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
console.log(chalk.green('\n✓ Docker Desktop installed'));
|
|
127
|
+
|
|
128
|
+
// Launch Docker Desktop
|
|
129
|
+
const { launch } = await inquirer.prompt([
|
|
130
|
+
{
|
|
131
|
+
type: 'confirm',
|
|
132
|
+
name: 'launch',
|
|
133
|
+
message: 'Launch Docker Desktop now?',
|
|
134
|
+
default: true
|
|
135
|
+
}
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
if (!launch) {
|
|
139
|
+
console.log(chalk.gray('\nPlease launch Docker Desktop manually and run "claude-phone setup" again.\n'));
|
|
140
|
+
return { success: false, cancelled: true };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
execSync('open -a Docker', { stdio: 'pipe' });
|
|
144
|
+
|
|
145
|
+
return await waitForDockerDaemon();
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(chalk.red(`\n❌ Installation failed: ${error.message}`));
|
|
148
|
+
return { success: false };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Guide user through manual Docker Desktop installation
|
|
154
|
+
* @returns {Promise<{success: boolean, cancelled?: boolean}>}
|
|
155
|
+
*/
|
|
156
|
+
async function installManually() {
|
|
157
|
+
console.log(chalk.cyan('\n📥 Manual Installation\n'));
|
|
158
|
+
console.log(chalk.gray('1. Download Docker Desktop from:'));
|
|
159
|
+
console.log(chalk.cyan(' https://www.docker.com/products/docker-desktop/\n'));
|
|
160
|
+
console.log(chalk.gray('2. Open the .dmg file and drag Docker to Applications'));
|
|
161
|
+
console.log(chalk.gray('3. Launch Docker from Applications\n'));
|
|
162
|
+
|
|
163
|
+
const { ready } = await inquirer.prompt([
|
|
164
|
+
{
|
|
165
|
+
type: 'confirm',
|
|
166
|
+
name: 'ready',
|
|
167
|
+
message: 'Press Enter when Docker Desktop is installed and running',
|
|
168
|
+
default: true
|
|
169
|
+
}
|
|
170
|
+
]);
|
|
171
|
+
|
|
172
|
+
if (!ready) {
|
|
173
|
+
return { success: false, cancelled: true };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Wait for Docker daemon
|
|
177
|
+
return await waitForDockerDaemon();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Wait for Docker daemon to become available
|
|
182
|
+
* @param {number} timeout - Timeout in milliseconds (default: 5 minutes)
|
|
183
|
+
* @returns {Promise<{success: boolean}>}
|
|
184
|
+
*/
|
|
185
|
+
async function waitForDockerDaemon(timeout = 300000) {
|
|
186
|
+
const spinner = ora('Waiting for Docker daemon...').start();
|
|
187
|
+
const startTime = Date.now();
|
|
188
|
+
const checkInterval = 5000; // Check every 5 seconds
|
|
189
|
+
|
|
190
|
+
while (Date.now() - startTime < timeout) {
|
|
191
|
+
// Check if Docker is running
|
|
192
|
+
const running = await isDockerRunning();
|
|
193
|
+
|
|
194
|
+
if (running) {
|
|
195
|
+
spinner.succeed('Docker daemon ready!');
|
|
196
|
+
return { success: true };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Update spinner with elapsed time
|
|
200
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
201
|
+
spinner.text = `Waiting for Docker daemon... (${elapsed}s)`;
|
|
202
|
+
|
|
203
|
+
// Wait before next check
|
|
204
|
+
await sleep(checkInterval);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Timeout
|
|
208
|
+
spinner.fail('Timeout waiting for Docker daemon');
|
|
209
|
+
console.log(chalk.yellow('\n⚠️ Docker Desktop is taking longer than expected to start.'));
|
|
210
|
+
console.log(chalk.gray('Please check Docker Desktop and run "claude-phone setup" again.\n'));
|
|
211
|
+
|
|
212
|
+
return { success: false };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if Docker daemon is running
|
|
217
|
+
* @returns {Promise<boolean>} True if running
|
|
218
|
+
*/
|
|
219
|
+
async function isDockerRunning() {
|
|
220
|
+
try {
|
|
221
|
+
execSync('docker ps', { stdio: 'pipe' });
|
|
222
|
+
return true;
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Check if Homebrew is installed
|
|
230
|
+
* @returns {boolean} True if Homebrew is available
|
|
231
|
+
*/
|
|
232
|
+
function checkHomebrew() {
|
|
233
|
+
try {
|
|
234
|
+
execSync('brew --version', { stdio: 'pipe' });
|
|
235
|
+
return true;
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Sleep for specified milliseconds
|
|
243
|
+
* @param {number} ms - Milliseconds to sleep
|
|
244
|
+
* @returns {Promise<void>}
|
|
245
|
+
*/
|
|
246
|
+
function sleep(ms) {
|
|
247
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
248
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import { withSudo } from '../utils/sudo.js';
|
|
5
|
+
import { checkDocker } from '../checks/docker.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Install Docker on Linux
|
|
9
|
+
* @param {object} platform - Platform info from detectPlatform()
|
|
10
|
+
* @returns {Promise<{success: boolean, cancelled?: boolean}>}
|
|
11
|
+
*/
|
|
12
|
+
export async function installDocker(platform) {
|
|
13
|
+
console.log(chalk.bold.cyan('\n📦 Docker Installation\n'));
|
|
14
|
+
|
|
15
|
+
// Confirm installation
|
|
16
|
+
const { confirmed } = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'confirm',
|
|
19
|
+
name: 'confirmed',
|
|
20
|
+
message: 'Install Docker automatically?',
|
|
21
|
+
default: false
|
|
22
|
+
}
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
if (!confirmed) {
|
|
26
|
+
showDockerManualInstructions(platform);
|
|
27
|
+
return { success: false, cancelled: true };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Route to platform-specific installer
|
|
31
|
+
let result;
|
|
32
|
+
|
|
33
|
+
switch (platform.packageManager) {
|
|
34
|
+
case 'apt':
|
|
35
|
+
result = await installDockerApt();
|
|
36
|
+
break;
|
|
37
|
+
case 'dnf':
|
|
38
|
+
case 'yum':
|
|
39
|
+
result = await installDockerDnf(platform.packageManager);
|
|
40
|
+
break;
|
|
41
|
+
case 'pacman':
|
|
42
|
+
result = await installDockerPacman();
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
console.error(chalk.red(`\n❌ Unsupported package manager: ${platform.packageManager}`));
|
|
46
|
+
showDockerManualInstructions(platform);
|
|
47
|
+
return { success: false };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!result.success) {
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add user to docker group
|
|
55
|
+
await addUserToDockerGroup();
|
|
56
|
+
|
|
57
|
+
// Start Docker daemon
|
|
58
|
+
await startDockerDaemon();
|
|
59
|
+
|
|
60
|
+
// Verify installation
|
|
61
|
+
console.log(chalk.cyan('\n✓ Verifying Docker installation...'));
|
|
62
|
+
const check = await checkDocker(platform);
|
|
63
|
+
|
|
64
|
+
if (check.passed) {
|
|
65
|
+
console.log(chalk.green(`✓ Docker v${check.version} installed successfully\n`));
|
|
66
|
+
console.log(chalk.yellow('⚠️ You may need to log out and back in for group changes to take effect.'));
|
|
67
|
+
return { success: true };
|
|
68
|
+
} else {
|
|
69
|
+
console.error(chalk.red('\n❌ Docker installation verification failed'));
|
|
70
|
+
return { success: false };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Install Docker on Ubuntu/Debian
|
|
76
|
+
* @returns {Promise<{success: boolean}>}
|
|
77
|
+
*/
|
|
78
|
+
async function installDockerApt() {
|
|
79
|
+
console.log(chalk.cyan('\nInstalling Docker via apt...'));
|
|
80
|
+
|
|
81
|
+
const commands = [
|
|
82
|
+
// Install prerequisites
|
|
83
|
+
'apt-get update',
|
|
84
|
+
'apt-get install -y ca-certificates curl gnupg',
|
|
85
|
+
|
|
86
|
+
// Add Docker GPG key
|
|
87
|
+
'install -m 0755 -d /etc/apt/keyrings',
|
|
88
|
+
'curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc',
|
|
89
|
+
'chmod a+r /etc/apt/keyrings/docker.asc',
|
|
90
|
+
|
|
91
|
+
// Add Docker repository
|
|
92
|
+
'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null',
|
|
93
|
+
|
|
94
|
+
// Install Docker
|
|
95
|
+
'apt-get update',
|
|
96
|
+
'apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin'
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return await withSudo(commands);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Install Docker on RHEL/Fedora
|
|
104
|
+
* @param {string} packageManager - 'dnf' or 'yum'
|
|
105
|
+
* @returns {Promise<{success: boolean}>}
|
|
106
|
+
*/
|
|
107
|
+
async function installDockerDnf(packageManager) {
|
|
108
|
+
console.log(chalk.cyan(`\nInstalling Docker via ${packageManager}...`));
|
|
109
|
+
|
|
110
|
+
const commands = [
|
|
111
|
+
// Install prerequisites
|
|
112
|
+
`${packageManager} install -y ${packageManager}-plugins-core`,
|
|
113
|
+
|
|
114
|
+
// Add Docker repository
|
|
115
|
+
`${packageManager} config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo`,
|
|
116
|
+
|
|
117
|
+
// Install Docker
|
|
118
|
+
`${packageManager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
return await withSudo(commands);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Install Docker on Arch Linux
|
|
126
|
+
* @returns {Promise<{success: boolean}>}
|
|
127
|
+
*/
|
|
128
|
+
async function installDockerPacman() {
|
|
129
|
+
console.log(chalk.cyan('\nInstalling Docker via pacman...'));
|
|
130
|
+
|
|
131
|
+
const commands = [
|
|
132
|
+
'pacman -Sy --noconfirm docker docker-compose'
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
return await withSudo(commands);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Add current user to docker group
|
|
140
|
+
* @returns {Promise<void>}
|
|
141
|
+
*/
|
|
142
|
+
async function addUserToDockerGroup() {
|
|
143
|
+
try {
|
|
144
|
+
const username = execSync('whoami', { encoding: 'utf-8' }).trim();
|
|
145
|
+
|
|
146
|
+
console.log(chalk.cyan(`\nAdding user '${username}' to docker group...`));
|
|
147
|
+
|
|
148
|
+
const commands = [
|
|
149
|
+
'groupadd -f docker',
|
|
150
|
+
`usermod -aG docker ${username}`
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
await withSudo(commands, { skipConfirm: true });
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.log(chalk.yellow('⚠️ Could not add user to docker group'));
|
|
156
|
+
console.log(chalk.gray('You may need to run: sudo usermod -aG docker $USER'));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Start Docker daemon
|
|
162
|
+
* @returns {Promise<void>}
|
|
163
|
+
*/
|
|
164
|
+
async function startDockerDaemon() {
|
|
165
|
+
try {
|
|
166
|
+
console.log(chalk.cyan('\nStarting Docker daemon...'));
|
|
167
|
+
|
|
168
|
+
// Try systemctl first
|
|
169
|
+
try {
|
|
170
|
+
execSync('systemctl --version', { stdio: 'pipe' });
|
|
171
|
+
|
|
172
|
+
const commands = [
|
|
173
|
+
'systemctl enable docker',
|
|
174
|
+
'systemctl start docker'
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
await withSudo(commands, { skipConfirm: true });
|
|
178
|
+
} catch (e) {
|
|
179
|
+
// Fall back to service command
|
|
180
|
+
const commands = ['service docker start'];
|
|
181
|
+
await withSudo(commands, { skipConfirm: true });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
console.log(chalk.green('✓ Docker daemon started'));
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.log(chalk.yellow('⚠️ Could not start Docker daemon automatically'));
|
|
187
|
+
console.log(chalk.gray('Try running: sudo systemctl start docker'));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Show manual installation instructions for Docker
|
|
193
|
+
* @param {object} platform - Platform info
|
|
194
|
+
* @returns {void}
|
|
195
|
+
*/
|
|
196
|
+
function showDockerManualInstructions(platform) {
|
|
197
|
+
console.log(chalk.yellow('\n📋 Manual Docker Installation\n'));
|
|
198
|
+
|
|
199
|
+
switch (platform.packageManager) {
|
|
200
|
+
case 'apt':
|
|
201
|
+
console.log(chalk.gray('For Ubuntu/Debian:\n'));
|
|
202
|
+
console.log(chalk.cyan(' # Add Docker repository'));
|
|
203
|
+
console.log(chalk.cyan(' curl -fsSL https://get.docker.com -o get-docker.sh'));
|
|
204
|
+
console.log(chalk.cyan(' sudo sh get-docker.sh\n'));
|
|
205
|
+
console.log(chalk.cyan(' # Add user to docker group'));
|
|
206
|
+
console.log(chalk.cyan(' sudo usermod -aG docker $USER\n'));
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case 'dnf':
|
|
210
|
+
case 'yum':
|
|
211
|
+
console.log(chalk.gray('For RHEL/Fedora:\n'));
|
|
212
|
+
console.log(chalk.cyan(' sudo dnf install docker-ce docker-ce-cli containerd.io'));
|
|
213
|
+
console.log(chalk.cyan(' sudo systemctl start docker'));
|
|
214
|
+
console.log(chalk.cyan(' sudo usermod -aG docker $USER\n'));
|
|
215
|
+
break;
|
|
216
|
+
|
|
217
|
+
case 'pacman':
|
|
218
|
+
console.log(chalk.gray('For Arch Linux:\n'));
|
|
219
|
+
console.log(chalk.cyan(' sudo pacman -Sy docker docker-compose'));
|
|
220
|
+
console.log(chalk.cyan(' sudo systemctl start docker'));
|
|
221
|
+
console.log(chalk.cyan(' sudo usermod -aG docker $USER\n'));
|
|
222
|
+
break;
|
|
223
|
+
|
|
224
|
+
default:
|
|
225
|
+
console.log(chalk.gray('See: https://docs.docker.com/engine/install/\n'));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log(chalk.gray('After installation, log out and back in, then run "claude-phone setup" again.\n'));
|
|
229
|
+
}
|