espcli 0.0.1 → 0.0.2

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.
@@ -1,36 +0,0 @@
1
- import { clean } from '@/core/operations/clean';
2
- import { findProjectRoot } from '@/core/services/idf';
3
- import { createOperationId } from '@/core/emitter';
4
- import { logger } from '@/tui/logger';
5
-
6
- interface CleanOptions {
7
- full?: boolean;
8
- }
9
-
10
- export async function cleanCommand(options: CleanOptions): Promise<void> {
11
- const projectDir = await findProjectRoot(process.cwd());
12
-
13
- if (!projectDir) {
14
- logger.error('Not in an ESP-IDF project directory');
15
- process.exit(1);
16
- }
17
-
18
- const opId = createOperationId();
19
-
20
- logger.step(options.full ? 'Running fullclean...' : 'Cleaning build...');
21
-
22
- const result = await clean(
23
- {
24
- projectDir,
25
- full: options.full,
26
- },
27
- opId
28
- );
29
-
30
- if (!result.ok) {
31
- logger.error(result.error);
32
- process.exit(1);
33
- }
34
-
35
- logger.success('Clean complete');
36
- }
@@ -1,18 +0,0 @@
1
- import { listDevices } from '@/core/operations/devices';
2
- import { deviceTable } from '@/tui/display';
3
- import { logger } from '@/tui/logger';
4
-
5
- export async function devicesCommand(): Promise<void> {
6
- logger.info('Scanning for devices...');
7
-
8
- const result = await listDevices();
9
-
10
- if (!result.ok) {
11
- logger.error(result.error);
12
- process.exit(1);
13
- }
14
-
15
- logger.newline();
16
- deviceTable(result.data);
17
- logger.newline();
18
- }
@@ -1,70 +0,0 @@
1
- import pc from 'picocolors';
2
- import { getHealth, type HealthStatus } from '@/core/services/health';
3
- import { listPorts } from '@/core/services/ports';
4
- import { logger } from '@/tui/logger';
5
-
6
- export async function doctorCommand(): Promise<void> {
7
- logger.info('Checking system health...');
8
- logger.newline();
9
-
10
- const health = await getHealth();
11
-
12
- // Display each check
13
- displayStatus(health.python);
14
- displayStatus(health.git);
15
- displayStatus(health.idf);
16
- displayStatus(health.pyserial);
17
- displayStatus(health.esptool);
18
-
19
- // Check for connected devices
20
- logger.newline();
21
- await checkDevices();
22
-
23
- // Summary
24
- logger.newline();
25
- const allOk = health.python.ok && health.git.ok && health.idf.ok && health.pyserial.ok && health.esptool.ok;
26
-
27
- if (allOk) {
28
- console.log(pc.green('✓ All checks passed! Ready for ESP32 development.'));
29
- } else {
30
- console.log(pc.yellow('⚠ Some checks failed. See hints above to fix issues.'));
31
- }
32
-
33
- logger.newline();
34
- }
35
-
36
- function displayStatus(status: HealthStatus): void {
37
- const icon = status.ok ? pc.green('✓') : pc.red('✗');
38
- const name = status.name.padEnd(10);
39
- const version = status.version ? pc.dim(`v${status.version}`) : '';
40
- const path = status.path ? pc.dim(` (${status.path})`) : '';
41
-
42
- if (status.ok) {
43
- console.log(` ${icon} ${pc.bold(name)} ${version}${path}`);
44
- } else {
45
- console.log(` ${icon} ${pc.bold(name)} ${pc.red(status.error || 'Not found')}`);
46
- if (status.hint) {
47
- console.log(` ${pc.dim('→')} ${pc.yellow(status.hint)}`);
48
- }
49
- }
50
- }
51
-
52
- async function checkDevices(): Promise<void> {
53
- console.log(pc.bold(' Devices:'));
54
-
55
- try {
56
- const devices = await listPorts();
57
-
58
- if (devices.length === 0) {
59
- console.log(` ${pc.dim('No ESP devices connected')}`);
60
- } else {
61
- for (const device of devices) {
62
- const chip = device.espChip || device.chip || 'Unknown';
63
- const type = device.connectionType === 'native-usb' ? 'USB' : 'UART';
64
- console.log(` ${pc.green('•')} ${device.port} ${pc.dim(`(${chip} via ${type})`)}`);
65
- }
66
- }
67
- } catch {
68
- console.log(` ${pc.dim('Could not check devices')}`);
69
- }
70
- }
@@ -1,61 +0,0 @@
1
- import { flash } from '@/core/operations/flash';
2
- import { listDevices } from '@/core/operations/devices';
3
- import { findProjectRoot } from '@/core/services/idf';
4
- import { emitter, createOperationId } from '@/core/emitter';
5
- import * as prompts from '@/tui/prompts';
6
- import { logger } from '@/tui/logger';
7
-
8
- interface FlashOptions {
9
- port?: string;
10
- baud?: number;
11
- }
12
-
13
- export async function flashCommand(options: FlashOptions): Promise<void> {
14
- const projectDir = await findProjectRoot(process.cwd());
15
-
16
- if (!projectDir) {
17
- logger.error('Not in an ESP-IDF project directory');
18
- process.exit(1);
19
- }
20
-
21
- let port = options.port;
22
-
23
- if (!port) {
24
- const devicesResult = await listDevices();
25
-
26
- if (!devicesResult.ok) {
27
- logger.error(devicesResult.error);
28
- process.exit(1);
29
- }
30
-
31
- port = await prompts.selectDevice(devicesResult.data);
32
- }
33
-
34
- const opId = createOperationId();
35
-
36
- emitter.subscribe(opId, (event) => {
37
- if (event.data.type === 'stdout' || event.data.type === 'stderr') {
38
- logger.output(event.data.text);
39
- }
40
- });
41
-
42
- logger.step(`Flashing to ${port}...`);
43
-
44
- const result = await flash(
45
- {
46
- projectDir,
47
- port,
48
- baud: options.baud,
49
- },
50
- opId
51
- );
52
-
53
- if (!result.ok) {
54
- logger.newline();
55
- logger.error(result.error);
56
- process.exit(1);
57
- }
58
-
59
- logger.newline();
60
- logger.success('Flash complete');
61
- }
@@ -1,59 +0,0 @@
1
- import { init } from '@/core/operations/init';
2
- import { createOperationId } from '@/core/emitter';
3
- import * as prompts from '@/tui/prompts';
4
- import { logger } from '@/tui/logger';
5
- import { resolve } from 'path';
6
-
7
- interface InitOptions {
8
- lang?: 'c' | 'cpp';
9
- target?: string;
10
- }
11
-
12
- export async function initCommand(name: string | undefined, options: InitOptions): Promise<void> {
13
- prompts.intro('Create ESP-IDF Project');
14
-
15
- const projectName = name || (await prompts.text('Project name', 'my-esp-project'));
16
-
17
- const language = options.lang || (await prompts.selectLanguage());
18
- const target = options.target || (await prompts.selectTarget());
19
-
20
- const config = {
21
- name: projectName,
22
- directory: resolve(process.cwd()),
23
- language,
24
- target,
25
- };
26
-
27
- prompts.note(
28
- `Name: ${config.name}\nLanguage: ${language.toUpperCase()}\nTarget: ${target}\nPath: ${config.directory}/${config.name}`,
29
- 'Project Configuration'
30
- );
31
-
32
- const proceed = await prompts.confirm('Create project?');
33
- if (!proceed) {
34
- prompts.cancel('Cancelled');
35
- return;
36
- }
37
-
38
- const spin = prompts.spinner();
39
- spin.start('Creating project...');
40
-
41
- const opId = createOperationId();
42
- const result = await init(config, opId);
43
-
44
- spin.stop();
45
-
46
- if (!result.ok) {
47
- logger.error(result.error);
48
- process.exit(1);
49
- }
50
-
51
- logger.success(`Created project at ${result.data.projectPath}`);
52
- logger.newline();
53
- logger.dim('Files created:');
54
- result.data.files.forEach((f) => logger.dim(` ${f}`));
55
- logger.newline();
56
- logger.info(`cd ${projectName} && espcli build`);
57
-
58
- prompts.outro('Project ready');
59
- }
@@ -1,89 +0,0 @@
1
- import { install } from '@/core/operations/install';
2
- import { getIdfStatus } from '@/core/services/idf';
3
- import { getShellInfo } from '@/core/services/shell';
4
- import { emitter, createOperationId } from '@/core/emitter';
5
- import { DEFAULT_ESP_PATH } from '@/core/constants';
6
- import * as prompts from '@/tui/prompts';
7
- import { logger } from '@/tui/logger';
8
-
9
- interface InstallOptions {
10
- path?: string;
11
- target?: string;
12
- yes?: boolean;
13
- }
14
-
15
- export async function installCommand(options: InstallOptions): Promise<void> {
16
- prompts.intro('ESP-IDF Installation');
17
-
18
- const status = await getIdfStatus();
19
-
20
- if (status.installed) {
21
- logger.info(`ESP-IDF already installed at ${status.path}`);
22
- if (status.version) {
23
- logger.info(`Version: ${status.version}`);
24
- }
25
-
26
- const reinstall = await prompts.confirm('Reinstall ESP-IDF?', false);
27
- if (!reinstall) {
28
- prompts.outro('Installation skipped');
29
- return;
30
- }
31
- }
32
-
33
- const path = options.path || DEFAULT_ESP_PATH;
34
- const target = options.target || 'all';
35
- const shellInfo = getShellInfo();
36
-
37
- if (!options.yes) {
38
- prompts.note(
39
- `Install path: ${path}\nTarget: ${target}\nShell: ${shellInfo.type}\nConfig: ${shellInfo.configPath}`,
40
- 'Installation Plan'
41
- );
42
-
43
- const proceed = await prompts.confirm('Proceed with installation?');
44
- if (!proceed) {
45
- prompts.cancel('Installation cancelled');
46
- return;
47
- }
48
- }
49
-
50
- const opId = createOperationId();
51
- const spin = prompts.spinner();
52
-
53
- emitter.subscribe(opId, (event) => {
54
- if (event.data.type === 'progress') {
55
- spin.message(event.data.message);
56
- } else if (event.data.type === 'stdout' || event.data.type === 'stderr') {
57
- spin.stop();
58
- logger.output(event.data.text);
59
- spin.start();
60
- }
61
- });
62
-
63
- spin.start('Installing ESP-IDF...');
64
-
65
- const result = await install(
66
- {
67
- path,
68
- target,
69
- addToShell: true,
70
- },
71
- opId
72
- );
73
-
74
- spin.stop();
75
-
76
- if (!result.ok) {
77
- logger.error(result.error);
78
- process.exit(1);
79
- }
80
-
81
- logger.success(`ESP-IDF installed at ${result.data.idfPath}`);
82
-
83
- if (result.data.addedToShell) {
84
- logger.info(`Added to ${shellInfo.configPath}`);
85
- logger.dim('Restart your shell or run: source ' + shellInfo.configPath);
86
- }
87
-
88
- prompts.outro('Installation complete');
89
- }
@@ -1,63 +0,0 @@
1
- import { startMonitor } from '@/core/operations/monitor';
2
- import { listDevices } from '@/core/operations/devices';
3
- import { findProjectRoot } from '@/core/services/idf';
4
- import { emitter, createOperationId } from '@/core/emitter';
5
- import * as prompts from '@/tui/prompts';
6
- import { logger } from '@/tui/logger';
7
-
8
- interface MonitorOptions {
9
- port?: string;
10
- baud?: number;
11
- }
12
-
13
- export async function monitorCommand(options: MonitorOptions): Promise<void> {
14
- const projectDir = await findProjectRoot(process.cwd());
15
-
16
- let port = options.port;
17
-
18
- if (!port) {
19
- const devicesResult = await listDevices();
20
-
21
- if (!devicesResult.ok) {
22
- logger.error(devicesResult.error);
23
- process.exit(1);
24
- }
25
-
26
- port = await prompts.selectDevice(devicesResult.data);
27
- }
28
-
29
- const opId = createOperationId();
30
-
31
- emitter.subscribe(opId, (event) => {
32
- if (event.data.type === 'stdout' || event.data.type === 'stderr') {
33
- logger.output(event.data.text);
34
- }
35
- });
36
-
37
- logger.step(`Connecting to ${port}...`);
38
- logger.dim('Press Ctrl+C to exit');
39
- logger.newline();
40
-
41
- const result = startMonitor(
42
- {
43
- port,
44
- baud: options.baud,
45
- projectDir: projectDir || undefined,
46
- },
47
- opId
48
- );
49
-
50
- if (!result.ok) {
51
- logger.error(result.error);
52
- process.exit(1);
53
- }
54
-
55
- process.on('SIGINT', () => {
56
- result.data.stop();
57
- logger.newline();
58
- logger.info('Monitor stopped');
59
- process.exit(0);
60
- });
61
-
62
- await new Promise(() => {});
63
- }
@@ -1,99 +0,0 @@
1
- import { build } from '@/core/operations/build';
2
- import { flash } from '@/core/operations/flash';
3
- import { startMonitor } from '@/core/operations/monitor';
4
- import { listDevices } from '@/core/operations/devices';
5
- import { findProjectRoot } from '@/core/services/idf';
6
- import { emitter, createOperationId } from '@/core/emitter';
7
- import * as prompts from '@/tui/prompts';
8
- import { logger } from '@/tui/logger';
9
-
10
- interface RunOptions {
11
- port?: string;
12
- baud?: number;
13
- skipBuild?: boolean;
14
- }
15
-
16
- export async function runCommand(options: RunOptions): Promise<void> {
17
- const projectDir = await findProjectRoot(process.cwd());
18
-
19
- if (!projectDir) {
20
- logger.error('Not in an ESP-IDF project directory');
21
- process.exit(1);
22
- }
23
-
24
- let port = options.port;
25
-
26
- if (!port) {
27
- const devicesResult = await listDevices();
28
-
29
- if (!devicesResult.ok) {
30
- logger.error(devicesResult.error);
31
- process.exit(1);
32
- }
33
-
34
- port = await prompts.selectDevice(devicesResult.data);
35
- }
36
-
37
- const streamHandler = (event: { data: { type: string; text?: string } }) => {
38
- if (event.data.type === 'stdout' || event.data.type === 'stderr') {
39
- logger.output((event.data as { text: string }).text);
40
- }
41
- };
42
-
43
- if (!options.skipBuild) {
44
- const buildOpId = createOperationId();
45
- emitter.subscribe(buildOpId, streamHandler);
46
-
47
- logger.step('Building...');
48
-
49
- const buildResult = await build({ projectDir }, buildOpId);
50
-
51
- if (!buildResult.ok) {
52
- logger.newline();
53
- logger.error(buildResult.error);
54
- process.exit(1);
55
- }
56
-
57
- logger.newline();
58
- logger.success('Build complete');
59
- }
60
-
61
- const flashOpId = createOperationId();
62
- emitter.subscribe(flashOpId, streamHandler);
63
-
64
- logger.step(`Flashing to ${port}...`);
65
-
66
- const flashResult = await flash({ projectDir, port, baud: options.baud }, flashOpId);
67
-
68
- if (!flashResult.ok) {
69
- logger.newline();
70
- logger.error(flashResult.error);
71
- process.exit(1);
72
- }
73
-
74
- logger.newline();
75
- logger.success('Flash complete');
76
-
77
- const monitorOpId = createOperationId();
78
- emitter.subscribe(monitorOpId, streamHandler);
79
-
80
- logger.step('Starting monitor...');
81
- logger.dim('Press Ctrl+C to exit');
82
- logger.newline();
83
-
84
- const monitorResult = startMonitor({ port, baud: options.baud, projectDir }, monitorOpId);
85
-
86
- if (!monitorResult.ok) {
87
- logger.error(monitorResult.error);
88
- process.exit(1);
89
- }
90
-
91
- process.on('SIGINT', () => {
92
- monitorResult.data.stop();
93
- logger.newline();
94
- logger.info('Monitor stopped');
95
- process.exit(0);
96
- });
97
-
98
- await new Promise(() => {});
99
- }
@@ -1,96 +0,0 @@
1
- import pc from 'picocolors';
2
- import type { SerialDevice, EspTarget } from '@/core/types';
3
-
4
- function stripAnsi(str: string): string {
5
- return str.replace(/\x1b\[[0-9;]*m/g, '');
6
- }
7
-
8
- function visibleLength(str: string): number {
9
- return stripAnsi(str).length;
10
- }
11
-
12
- export function table(headers: string[], rows: string[][]): void {
13
- const colWidths = headers.map((h, i) => {
14
- const maxRow = Math.max(...rows.map((r) => visibleLength(r[i] || '')));
15
- return Math.max(h.length, maxRow);
16
- });
17
-
18
- const separator = '─';
19
- const topBorder = '┌' + colWidths.map((w) => separator.repeat(w + 2)).join('┬') + '┐';
20
- const midBorder = '├' + colWidths.map((w) => separator.repeat(w + 2)).join('┼') + '┤';
21
- const botBorder = '└' + colWidths.map((w) => separator.repeat(w + 2)).join('┴') + '┘';
22
-
23
- const padCell = (cell: string, width: number) => {
24
- const visible = visibleLength(cell);
25
- const padding = width - visible;
26
- return cell + ' '.repeat(Math.max(0, padding));
27
- };
28
-
29
- const formatRow = (cells: string[]) =>
30
- '│' + cells.map((c, i) => ` ${padCell(c || '', colWidths[i])} `).join('│') + '│';
31
-
32
- console.log(pc.dim(topBorder));
33
- console.log(pc.bold(formatRow(headers)));
34
- console.log(pc.dim(midBorder));
35
- rows.forEach((row) => console.log(formatRow(row)));
36
- console.log(pc.dim(botBorder));
37
- }
38
-
39
- export function deviceTable(devices: SerialDevice[]): void {
40
- if (devices.length === 0) {
41
- console.log(pc.dim('No devices found'));
42
- return;
43
- }
44
-
45
- const headers = ['Port', 'Type', 'Chip', 'ESP'];
46
- const rows = devices.map((d) => {
47
- const typeLabel = getConnectionTypeLabel(d.connectionType);
48
- const espLabel = d.espChip || (d.connectionType === 'native-usb' ? '?' : '-');
49
- return [d.port, typeLabel, d.chip || '-', espLabel];
50
- });
51
-
52
- table(headers, rows);
53
-
54
- // Show helpful hints
55
- const hasNativeUsb = devices.some((d) => d.connectionType === 'native-usb');
56
- const hasUartBridge = devices.some((d) => d.connectionType === 'uart-bridge');
57
-
58
- if (hasNativeUsb && hasUartBridge) {
59
- console.log('');
60
- console.log(pc.dim(' Hint: Multiple ports from same board'));
61
- console.log(pc.dim(' • ') + pc.cyan('UART') + pc.dim(' - Reliable flashing & monitoring'));
62
- console.log(pc.dim(' • ') + pc.magenta('USB') + pc.dim(' - JTAG debugging'));
63
- }
64
- }
65
-
66
- function getConnectionTypeLabel(type: SerialDevice['connectionType']): string {
67
- switch (type) {
68
- case 'native-usb':
69
- return pc.magenta('USB');
70
- case 'uart-bridge':
71
- return pc.cyan('UART');
72
- default:
73
- return pc.dim('?');
74
- }
75
- }
76
-
77
- export function targetList(targets: EspTarget[]): void {
78
- targets.forEach((t) => {
79
- const status = t.stable ? pc.green('stable') : pc.yellow('preview');
80
- console.log(` ${pc.bold(t.id.padEnd(10))} ${pc.dim(t.description)} [${status}]`);
81
- });
82
- }
83
-
84
- export function box(title: string, content: string): void {
85
- const lines = content.split('\n');
86
- const maxLen = Math.max(title.length, ...lines.map((l) => l.length));
87
- const width = maxLen + 4;
88
-
89
- console.log(pc.dim('╭' + '─'.repeat(width) + '╮'));
90
- console.log(pc.dim('│') + ' ' + pc.bold(title.padEnd(maxLen + 2)) + ' ' + pc.dim('│'));
91
- console.log(pc.dim('├' + '─'.repeat(width) + '┤'));
92
- lines.forEach((line) => {
93
- console.log(pc.dim('│') + ' ' + line.padEnd(maxLen + 1) + ' ' + pc.dim('│'));
94
- });
95
- console.log(pc.dim('╰' + '─'.repeat(width) + '╯'));
96
- }
package/src/tui/index.ts DELETED
@@ -1,96 +0,0 @@
1
- import { Command } from 'commander';
2
- import { VERSION } from '@/core/constants';
3
- import { installCommand } from '@/tui/commands/install';
4
- import { initCommand } from '@/tui/commands/init';
5
- import { devicesCommand } from '@/tui/commands/devices';
6
- import { buildCommand } from '@/tui/commands/build';
7
- import { flashCommand } from '@/tui/commands/flash';
8
- import { monitorCommand } from '@/tui/commands/monitor';
9
- import { cleanCommand } from '@/tui/commands/clean';
10
- import { runCommand } from '@/tui/commands/run';
11
- import { doctorCommand } from '@/tui/commands/doctor';
12
- // import { createServer } from '@/server';
13
-
14
- export function createCli(): Command {
15
- const program = new Command();
16
-
17
- program
18
- .name('espcli')
19
- .description('CLI tool for ESP-IDF development')
20
- .version(VERSION);
21
-
22
- program
23
- .command('install')
24
- .description('Install ESP-IDF framework')
25
- .option('-p, --path <path>', 'Installation path')
26
- .option('-t, --target <target>', 'Target chip (default: all)')
27
- .option('-y, --yes', 'Skip confirmation prompts')
28
- .action(installCommand);
29
-
30
- program
31
- .command('init [name]')
32
- .description('Create a new ESP-IDF project')
33
- .option('-l, --lang <lang>', 'Language: c or cpp')
34
- .option('-t, --target <target>', 'Target chip')
35
- .action(initCommand);
36
-
37
- program
38
- .command('devices')
39
- .alias('ports')
40
- .description('List connected ESP devices')
41
- .action(devicesCommand);
42
-
43
- program
44
- .command('build')
45
- .description('Build the current project')
46
- .option('-t, --target <target>', 'Set target before build')
47
- .option('-c, --clean', 'Clean before build')
48
- .action(buildCommand);
49
-
50
- program
51
- .command('flash')
52
- .description('Flash firmware to device')
53
- .option('-p, --port <port>', 'Serial port')
54
- .option('-b, --baud <baud>', 'Flash baud rate', parseInt)
55
- .action(flashCommand);
56
-
57
- program
58
- .command('monitor')
59
- .description('Open serial monitor')
60
- .option('-p, --port <port>', 'Serial port')
61
- .option('-b, --baud <baud>', 'Monitor baud rate', parseInt)
62
- .action(monitorCommand);
63
-
64
- program
65
- .command('run')
66
- .description('Build, flash, and monitor')
67
- .option('-p, --port <port>', 'Serial port')
68
- .option('-b, --baud <baud>', 'Baud rate', parseInt)
69
- .option('--skip-build', 'Skip build step')
70
- .action(runCommand);
71
-
72
- program
73
- .command('clean')
74
- .description('Clean build artifacts')
75
- .option('-f, --full', 'Full clean (includes sdkconfig)')
76
- .action(cleanCommand);
77
-
78
- // program
79
- // .command('serve')
80
- // .description('Start HTTP/WebSocket server for GUI')
81
- // .option('-p, --port <port>', 'Server port', '3000')
82
- // .action((options: { port: string }) => {
83
- // const port = parseInt(options.port, 10);
84
- // const server = createServer(port);
85
- // console.log(`ESP CLI server running on http://localhost:${server.port}`);
86
- // console.log(`WebSocket available at ws://localhost:${server.port}/ws`);
87
- // });
88
-
89
- program
90
- .command('doctor')
91
- .alias('check')
92
- .description('Check system health and dependencies')
93
- .action(doctorCommand);
94
-
95
- return program;
96
- }