@jetstart/cli 1.1.1
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/.eslintrc.json +6 -0
- package/README.md +92 -0
- package/bin/jetstart.js +3 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +74 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +12 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +53 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/create.d.ts +12 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +95 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +13 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +152 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +15 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/logs.d.ts +13 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +103 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +24 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/prompt.d.ts +10 -0
- package/dist/utils/prompt.d.ts.map +1 -0
- package/dist/utils/prompt.js +53 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/utils/spinner.d.ts +9 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +31 -0
- package/dist/utils/spinner.js.map +1 -0
- package/dist/utils/template.d.ts +7 -0
- package/dist/utils/template.d.ts.map +1 -0
- package/dist/utils/template.js +516 -0
- package/dist/utils/template.js.map +1 -0
- package/package.json +80 -0
- package/src/cli.ts +80 -0
- package/src/commands/build.ts +60 -0
- package/src/commands/create.ts +109 -0
- package/src/commands/dev.ts +138 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/logs.ts +117 -0
- package/src/index.ts +17 -0
- package/src/types/index.ts +22 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/logger.ts +42 -0
- package/src/utils/prompt.ts +56 -0
- package/src/utils/spinner.ts +25 -0
- package/src/utils/template.ts +635 -0
- package/tests/create.test.ts +33 -0
- package/tests/utils.test.ts +17 -0
- package/tsconfig.json +25 -0
package/src/cli.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CLI Entry Point
|
|
5
|
+
* Defines all commands and handles command execution
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import { createCommand } from './commands/create';
|
|
11
|
+
import { devCommand } from './commands/dev';
|
|
12
|
+
import { buildCommand } from './commands/build';
|
|
13
|
+
import { logsCommand } from './commands/logs';
|
|
14
|
+
import { JETSTART_VERSION } from '@jetstart/shared';
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('jetstart')
|
|
20
|
+
.description('Fast, wireless Android development with Kotlin and Jetpack Compose')
|
|
21
|
+
.version(JETSTART_VERSION);
|
|
22
|
+
|
|
23
|
+
// Create command
|
|
24
|
+
program
|
|
25
|
+
.command('create <name>')
|
|
26
|
+
.description('Create a new JetStart project')
|
|
27
|
+
.option('-p, --package <name>', 'Package name (e.g., com.example.app)')
|
|
28
|
+
.option('-t, --template <name>', 'Template to use', 'default')
|
|
29
|
+
.option('--skip-install', 'Skip npm install')
|
|
30
|
+
.action(createCommand);
|
|
31
|
+
|
|
32
|
+
// Dev command
|
|
33
|
+
program
|
|
34
|
+
.command('dev')
|
|
35
|
+
.description('Start development server')
|
|
36
|
+
.option('-p, --port <port>', 'Port for dev server', '8765')
|
|
37
|
+
.option('-H, --host <host>', 'Host address (defaults to auto-detected network IP)')
|
|
38
|
+
.option('--no-qr', 'Do not show QR code')
|
|
39
|
+
.option('--no-open', 'Do not open browser')
|
|
40
|
+
.action(devCommand);
|
|
41
|
+
|
|
42
|
+
// Build command
|
|
43
|
+
program
|
|
44
|
+
.command('build')
|
|
45
|
+
.description('Build production APK')
|
|
46
|
+
.option('-o, --output <path>', 'Output directory', './build')
|
|
47
|
+
.option('-r, --release', 'Build release version', false)
|
|
48
|
+
.option('--sign', 'Sign the APK')
|
|
49
|
+
.action(buildCommand);
|
|
50
|
+
|
|
51
|
+
// Logs command
|
|
52
|
+
program
|
|
53
|
+
.command('logs')
|
|
54
|
+
.description('Stream application logs')
|
|
55
|
+
.option('-f, --follow', 'Follow log output', true)
|
|
56
|
+
.option('-l, --level <level>', 'Filter by log level')
|
|
57
|
+
.option('-s, --source <source>', 'Filter by log source')
|
|
58
|
+
.option('-n, --lines <number>', 'Number of lines to show', '100')
|
|
59
|
+
.action(logsCommand);
|
|
60
|
+
|
|
61
|
+
// Error handling
|
|
62
|
+
program.on('command:*', (operands) => {
|
|
63
|
+
console.error(chalk.red(`Error: Unknown command '${operands[0]}'`));
|
|
64
|
+
console.log(chalk.yellow('\nRun "jetstart --help" for available commands'));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Handle errors gracefully
|
|
69
|
+
process.on('unhandledRejection', (err: Error) => {
|
|
70
|
+
console.error(chalk.red('Unhandled error:'), err.message);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Parse arguments
|
|
75
|
+
program.parse(process.argv);
|
|
76
|
+
|
|
77
|
+
// Show help if no command provided
|
|
78
|
+
if (!process.argv.slice(2).length) {
|
|
79
|
+
program.outputHelp();
|
|
80
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Command
|
|
3
|
+
* Builds production APK
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { log, success, error, info } from '../utils/logger';
|
|
9
|
+
import { startSpinner, stopSpinner } from '../utils/spinner';
|
|
10
|
+
import { BuildType } from '@jetstart/shared';
|
|
11
|
+
|
|
12
|
+
interface BuildOptions {
|
|
13
|
+
output?: string;
|
|
14
|
+
release?: boolean;
|
|
15
|
+
sign?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function buildCommand(options: BuildOptions) {
|
|
19
|
+
try {
|
|
20
|
+
const buildType = options.release ? BuildType.RELEASE : BuildType.DEBUG;
|
|
21
|
+
const outputDir = options.output || './build';
|
|
22
|
+
|
|
23
|
+
log(`Building ${buildType} APK...`);
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// Validate project
|
|
27
|
+
const spinner = startSpinner('Validating project...');
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
29
|
+
stopSpinner(spinner, true, 'Project validated');
|
|
30
|
+
|
|
31
|
+
// Compile Kotlin
|
|
32
|
+
const compileSpinner = startSpinner('Compiling Kotlin sources...');
|
|
33
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
34
|
+
stopSpinner(compileSpinner, true, 'Kotlin compiled');
|
|
35
|
+
|
|
36
|
+
// Package APK
|
|
37
|
+
const packageSpinner = startSpinner('Packaging APK...');
|
|
38
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
39
|
+
stopSpinner(packageSpinner, true, 'APK packaged');
|
|
40
|
+
|
|
41
|
+
// Sign APK (if release)
|
|
42
|
+
if (options.release && options.sign) {
|
|
43
|
+
const signSpinner = startSpinner('Signing APK...');
|
|
44
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
45
|
+
stopSpinner(signSpinner, true, 'APK signed');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log();
|
|
49
|
+
success('Build completed successfully!');
|
|
50
|
+
console.log();
|
|
51
|
+
info(`Output: ${chalk.cyan(path.resolve(outputDir))}`);
|
|
52
|
+
info(`Size: ${chalk.cyan('5.2 MB')}`);
|
|
53
|
+
info(`Type: ${chalk.cyan(buildType)}`);
|
|
54
|
+
console.log();
|
|
55
|
+
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
error(`Build failed: ${err.message}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Command
|
|
3
|
+
* Scaffolds a new JetStart project
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { log, success, error } from '../utils/logger';
|
|
10
|
+
import { startSpinner, stopSpinner } from '../utils/spinner';
|
|
11
|
+
import { prompt } from '../utils/prompt';
|
|
12
|
+
import { generateProjectTemplate } from '../utils/template';
|
|
13
|
+
import { isValidProjectName, isValidPackageName } from '@jetstart/shared';
|
|
14
|
+
|
|
15
|
+
interface CreateOptions {
|
|
16
|
+
package?: string;
|
|
17
|
+
template?: string;
|
|
18
|
+
skipInstall?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function createCommand(name: string, options: CreateOptions) {
|
|
22
|
+
try {
|
|
23
|
+
// Validate project name
|
|
24
|
+
if (!isValidProjectName(name)) {
|
|
25
|
+
error('Invalid project name. Use letters, numbers, hyphens, and underscores only.');
|
|
26
|
+
error('Project name must start with a letter and be 1-64 characters long.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const projectPath = path.resolve(process.cwd(), name);
|
|
31
|
+
|
|
32
|
+
// Check if directory already exists
|
|
33
|
+
if (await fs.pathExists(projectPath)) {
|
|
34
|
+
error(`Directory '${name}' already exists!`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
log(`Creating JetStart project: ${chalk.cyan(name)}`);
|
|
39
|
+
console.log();
|
|
40
|
+
|
|
41
|
+
// Get package name
|
|
42
|
+
let packageName = options.package;
|
|
43
|
+
if (!packageName) {
|
|
44
|
+
const defaultPackage = `com.jetstart.${name.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
|
|
45
|
+
const answer = await prompt({
|
|
46
|
+
type: 'input',
|
|
47
|
+
name: 'packageName',
|
|
48
|
+
message: 'Package name:',
|
|
49
|
+
default: defaultPackage,
|
|
50
|
+
validate: (input: string) => {
|
|
51
|
+
if (!isValidPackageName(input)) {
|
|
52
|
+
return 'Invalid package name. Use format: com.company.app';
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
packageName = answer.packageName;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Validate package name
|
|
61
|
+
if (!isValidPackageName(packageName!)) {
|
|
62
|
+
error('Invalid package name. Use format: com.company.app');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Create project structure
|
|
67
|
+
const spinner = startSpinner('Creating project structure...');
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await fs.ensureDir(projectPath);
|
|
71
|
+
|
|
72
|
+
// Generate project files
|
|
73
|
+
await generateProjectTemplate(projectPath, {
|
|
74
|
+
projectName: name,
|
|
75
|
+
packageName: packageName!,
|
|
76
|
+
template: options.template || 'default',
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
stopSpinner(spinner, true, 'Project structure created');
|
|
80
|
+
|
|
81
|
+
// Install dependencies
|
|
82
|
+
if (!options.skipInstall) {
|
|
83
|
+
const installSpinner = startSpinner('Installing dependencies...');
|
|
84
|
+
|
|
85
|
+
// In a real implementation, we'd run npm/gradle here
|
|
86
|
+
// For now, we'll simulate it
|
|
87
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
88
|
+
|
|
89
|
+
stopSpinner(installSpinner, true, 'Dependencies installed');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log();
|
|
93
|
+
success(`Successfully created project: ${chalk.cyan(name)}`);
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(chalk.bold('Next steps:'));
|
|
96
|
+
console.log(` ${chalk.cyan('cd')} ${name}`);
|
|
97
|
+
console.log(` ${chalk.cyan('jetstart dev')}`);
|
|
98
|
+
console.log();
|
|
99
|
+
|
|
100
|
+
} catch (err: any) {
|
|
101
|
+
stopSpinner(spinner, false, 'Failed to create project');
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
} catch (err: any) {
|
|
106
|
+
error(`Failed to create project: ${err.message}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dev Command
|
|
3
|
+
* Starts the development server
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import qrcode from 'qrcode-terminal';
|
|
10
|
+
import { log, success, error, info } from '../utils/logger';
|
|
11
|
+
import { JetStartServer } from '@jetstart/core';
|
|
12
|
+
import { DEFAULT_CORE_PORT, DEFAULT_WS_PORT } from '@jetstart/shared';
|
|
13
|
+
|
|
14
|
+
interface DevOptions {
|
|
15
|
+
port?: string;
|
|
16
|
+
host?: string;
|
|
17
|
+
qr?: boolean;
|
|
18
|
+
open?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function devCommand(options: DevOptions) {
|
|
22
|
+
try {
|
|
23
|
+
const port = parseInt(options.port || String(DEFAULT_CORE_PORT));
|
|
24
|
+
const wsPort = DEFAULT_WS_PORT;
|
|
25
|
+
const host = options.host || getLocalIP();
|
|
26
|
+
const showQR = options.qr !== false;
|
|
27
|
+
|
|
28
|
+
log('Starting JetStart development server...');
|
|
29
|
+
console.log();
|
|
30
|
+
|
|
31
|
+
// Get project path and name
|
|
32
|
+
const projectPath = process.cwd();
|
|
33
|
+
const projectName = path.basename(projectPath);
|
|
34
|
+
|
|
35
|
+
// Create and start Core server
|
|
36
|
+
// Bind to 0.0.0.0 to accept connections on all interfaces,
|
|
37
|
+
// but pass the detected IP for display and client connections
|
|
38
|
+
const server = new JetStartServer({
|
|
39
|
+
httpPort: port,
|
|
40
|
+
wsPort,
|
|
41
|
+
host: '0.0.0.0', // Bind to all interfaces for maximum compatibility
|
|
42
|
+
displayHost: host, // Use detected IP for display and client connections
|
|
43
|
+
projectPath,
|
|
44
|
+
projectName,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const session = await server.start();
|
|
48
|
+
|
|
49
|
+
// Get URLs
|
|
50
|
+
const serverUrl = `http://${host}:${port}`;
|
|
51
|
+
const wsUrl = `ws://${host}:${wsPort}`;
|
|
52
|
+
const localUrl = `http://localhost:${port}`;
|
|
53
|
+
|
|
54
|
+
console.log();
|
|
55
|
+
success('JetStart dev server is running!');
|
|
56
|
+
console.log();
|
|
57
|
+
info(`Local: ${chalk.cyan(localUrl)}`);
|
|
58
|
+
info(`Network: ${chalk.cyan(serverUrl)}`);
|
|
59
|
+
info(`Project: ${chalk.cyan(projectName)}`);
|
|
60
|
+
console.log();
|
|
61
|
+
|
|
62
|
+
// Generate QR code for mobile connection
|
|
63
|
+
if (showQR) {
|
|
64
|
+
const qrData = {
|
|
65
|
+
serverUrl,
|
|
66
|
+
wsUrl,
|
|
67
|
+
sessionId: session.id,
|
|
68
|
+
token: session.token,
|
|
69
|
+
projectName,
|
|
70
|
+
version: '0.1.0',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
console.log(chalk.bold('Scan QR or connect manually:'));
|
|
74
|
+
qrcode.generate(JSON.stringify(qrData), { small: true });
|
|
75
|
+
console.log();
|
|
76
|
+
info(`IP: ${chalk.cyan(host)}`);
|
|
77
|
+
info(`Session: ${chalk.dim(session.id)}`);
|
|
78
|
+
info(`Token: ${chalk.dim(session.token)}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
info('Watching for file changes...');
|
|
82
|
+
info('Press Ctrl+C to stop');
|
|
83
|
+
console.log();
|
|
84
|
+
|
|
85
|
+
// Handle graceful shutdown
|
|
86
|
+
process.on('SIGINT', async () => {
|
|
87
|
+
console.log();
|
|
88
|
+
log('Shutting down dev server...');
|
|
89
|
+
await server.stop();
|
|
90
|
+
process.exit(0);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
process.on('SIGTERM', async () => {
|
|
94
|
+
console.log();
|
|
95
|
+
log('Shutting down dev server...');
|
|
96
|
+
await server.stop();
|
|
97
|
+
process.exit(0);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Keep process alive
|
|
101
|
+
await new Promise(() => {}); // Keep alive forever
|
|
102
|
+
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
error(`Failed to start dev server: ${err.message}`);
|
|
105
|
+
console.error(err.stack);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getLocalIP(): string {
|
|
111
|
+
const interfaces = os.networkInterfaces();
|
|
112
|
+
const addresses: string[] = [];
|
|
113
|
+
|
|
114
|
+
for (const name of Object.keys(interfaces)) {
|
|
115
|
+
const iface = interfaces[name];
|
|
116
|
+
if (!iface) continue;
|
|
117
|
+
|
|
118
|
+
for (const alias of iface) {
|
|
119
|
+
// Handle both 'IPv4' string (Node.js 18+) and numeric 4 (older versions)
|
|
120
|
+
const isIPv4 = alias.family === 'IPv4' || (alias.family as any) === 4;
|
|
121
|
+
|
|
122
|
+
if (isIPv4 && !alias.internal) {
|
|
123
|
+
// Prefer addresses starting with 192.168 (typical home/hotspot network)
|
|
124
|
+
if (alias.address.startsWith('192.168')) {
|
|
125
|
+
return alias.address;
|
|
126
|
+
}
|
|
127
|
+
addresses.push(alias.address);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Return first non-internal IPv4 address found
|
|
133
|
+
if (addresses.length > 0) {
|
|
134
|
+
return addresses[0];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return 'localhost';
|
|
138
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logs Command
|
|
3
|
+
* Streams application logs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import WebSocket from 'ws';
|
|
8
|
+
import { log, error, info } from '../utils/logger';
|
|
9
|
+
import { LogLevel, LogSource, LogEntry, DEFAULT_LOGS_PORT } from '@jetstart/shared';
|
|
10
|
+
|
|
11
|
+
interface LogsOptions {
|
|
12
|
+
follow?: boolean;
|
|
13
|
+
level?: string;
|
|
14
|
+
source?: string;
|
|
15
|
+
lines?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function logsCommand(options: LogsOptions) {
|
|
19
|
+
try {
|
|
20
|
+
// const follow = options.follow !== false;
|
|
21
|
+
const maxLines = parseInt(options.lines || '100');
|
|
22
|
+
|
|
23
|
+
log('Connecting to JetStart logs service...');
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// Connect to logs service via WebSocket
|
|
27
|
+
const ws = new WebSocket(`ws://localhost:${DEFAULT_LOGS_PORT}`);
|
|
28
|
+
|
|
29
|
+
ws.on('open', () => {
|
|
30
|
+
info('Connected to logs service');
|
|
31
|
+
console.log();
|
|
32
|
+
|
|
33
|
+
// Send filter options
|
|
34
|
+
ws.send(JSON.stringify({
|
|
35
|
+
type: 'subscribe',
|
|
36
|
+
filter: {
|
|
37
|
+
levels: options.level ? [options.level] : undefined,
|
|
38
|
+
sources: options.source ? [options.source] : undefined,
|
|
39
|
+
},
|
|
40
|
+
maxLines,
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
ws.on('message', (data: string) => {
|
|
45
|
+
try {
|
|
46
|
+
const logEntry: LogEntry = JSON.parse(data);
|
|
47
|
+
formatLogEntry(logEntry);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.log(data);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
ws.on('error', (err) => {
|
|
54
|
+
error(`WebSocket error: ${err.message}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
ws.on('close', () => {
|
|
59
|
+
console.log();
|
|
60
|
+
info('Disconnected from logs service');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Handle Ctrl+C
|
|
65
|
+
process.on('SIGINT', () => {
|
|
66
|
+
console.log();
|
|
67
|
+
log('Closing connection...');
|
|
68
|
+
ws.close();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
} catch (err: any) {
|
|
72
|
+
error(`Failed to connect to logs: ${err.message}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatLogEntry(entry: LogEntry) {
|
|
78
|
+
const timestamp = new Date(entry.timestamp).toLocaleTimeString();
|
|
79
|
+
const level = formatLogLevel(entry.level);
|
|
80
|
+
const source = formatLogSource(entry.source);
|
|
81
|
+
const tag = chalk.gray(`[${entry.tag}]`);
|
|
82
|
+
|
|
83
|
+
console.log(`${chalk.gray(timestamp)} ${level} ${source} ${tag} ${entry.message}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function formatLogLevel(level: LogLevel): string {
|
|
87
|
+
switch (level) {
|
|
88
|
+
case LogLevel.VERBOSE:
|
|
89
|
+
return chalk.gray('VERBOSE');
|
|
90
|
+
case LogLevel.DEBUG:
|
|
91
|
+
return chalk.blue('DEBUG');
|
|
92
|
+
case LogLevel.INFO:
|
|
93
|
+
return chalk.green('INFO');
|
|
94
|
+
case LogLevel.WARN:
|
|
95
|
+
return chalk.yellow('WARN');
|
|
96
|
+
case LogLevel.ERROR:
|
|
97
|
+
return chalk.red('ERROR');
|
|
98
|
+
case LogLevel.FATAL:
|
|
99
|
+
return chalk.bgRed.white('FATAL');
|
|
100
|
+
default:
|
|
101
|
+
return level;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function formatLogSource(source: LogSource): string {
|
|
106
|
+
const sourceColors: Record<LogSource, (text: string) => string> = {
|
|
107
|
+
[LogSource.CLI]: chalk.cyan,
|
|
108
|
+
[LogSource.CORE]: chalk.magenta,
|
|
109
|
+
[LogSource.CLIENT]: chalk.green,
|
|
110
|
+
[LogSource.BUILD]: chalk.yellow,
|
|
111
|
+
[LogSource.NETWORK]: chalk.blue,
|
|
112
|
+
[LogSource.SYSTEM]: chalk.gray,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const colorFn = sourceColors[source] || chalk.white;
|
|
116
|
+
return colorFn(`[${source}]`);
|
|
117
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for programmatic API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from './commands';
|
|
6
|
+
export * from './types';
|
|
7
|
+
export * from './utils';
|
|
8
|
+
|
|
9
|
+
// Re-export shared types that CLI users might need
|
|
10
|
+
export type {
|
|
11
|
+
Session,
|
|
12
|
+
SessionStatus,
|
|
13
|
+
BuildConfig,
|
|
14
|
+
BuildResult,
|
|
15
|
+
DeviceInfo,
|
|
16
|
+
LogLevel,
|
|
17
|
+
} from '@jetstart/shared';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI-specific types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface ProjectConfig {
|
|
6
|
+
projectName: string;
|
|
7
|
+
packageName: string;
|
|
8
|
+
template: string;
|
|
9
|
+
minSdkVersion?: number;
|
|
10
|
+
targetSdkVersion?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TemplateOptions {
|
|
14
|
+
projectName: string;
|
|
15
|
+
packageName: string;
|
|
16
|
+
template: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CommandContext {
|
|
20
|
+
cwd: string;
|
|
21
|
+
config?: ProjectConfig;
|
|
22
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility
|
|
3
|
+
* Formatted console output with colors
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
export function log(message: string) {
|
|
9
|
+
console.log(message);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function info(message: string) {
|
|
13
|
+
console.log(chalk.blue('ℹ'), message);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function success(message: string) {
|
|
17
|
+
console.log(chalk.green('✔'), message);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function warning(message: string) {
|
|
21
|
+
console.log(chalk.yellow('⚠'), message);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function error(message: string) {
|
|
25
|
+
console.error(chalk.red('✖'), message);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function debug(message: string) {
|
|
29
|
+
if (process.env.DEBUG) {
|
|
30
|
+
console.log(chalk.gray('[DEBUG]'), message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function banner() {
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(chalk.cyan.bold(' ╔═╗┌─┐┌┬┐╔═╗┌┬┐┌─┐┬─┐┌┬┐'));
|
|
37
|
+
console.log(chalk.cyan.bold(' ║├┤ │ ╚═╗ │ ├─┤├┬┘ │ '));
|
|
38
|
+
console.log(chalk.cyan.bold(' ╚═╝└─┘ ┴ ╚═╝ ┴ ┴ ┴┴└─ ┴ '));
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(chalk.gray(' Fast, wireless Android development'));
|
|
41
|
+
console.log();
|
|
42
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Utility
|
|
3
|
+
* User input prompts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import inquirer from 'inquirer';
|
|
7
|
+
|
|
8
|
+
export async function prompt(question: inquirer.QuestionCollection) {
|
|
9
|
+
return inquirer.prompt([question]);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function confirm(message: string, defaultValue = true): Promise<boolean> {
|
|
13
|
+
const answer = await inquirer.prompt([
|
|
14
|
+
{
|
|
15
|
+
type: 'confirm',
|
|
16
|
+
name: 'confirmed',
|
|
17
|
+
message,
|
|
18
|
+
default: defaultValue,
|
|
19
|
+
},
|
|
20
|
+
]);
|
|
21
|
+
return answer.confirmed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function select(
|
|
25
|
+
message: string,
|
|
26
|
+
choices: string[],
|
|
27
|
+
defaultValue?: string
|
|
28
|
+
): Promise<string> {
|
|
29
|
+
const answer = await inquirer.prompt([
|
|
30
|
+
{
|
|
31
|
+
type: 'list',
|
|
32
|
+
name: 'selected',
|
|
33
|
+
message,
|
|
34
|
+
choices,
|
|
35
|
+
default: defaultValue,
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
return answer.selected;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function input(
|
|
42
|
+
message: string,
|
|
43
|
+
defaultValue?: string,
|
|
44
|
+
validate?: (input: string) => boolean | string
|
|
45
|
+
): Promise<string> {
|
|
46
|
+
const answer = await inquirer.prompt([
|
|
47
|
+
{
|
|
48
|
+
type: 'input',
|
|
49
|
+
name: 'value',
|
|
50
|
+
message,
|
|
51
|
+
default: defaultValue,
|
|
52
|
+
validate,
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
return answer.value;
|
|
56
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Utility
|
|
3
|
+
* Loading spinners for long-running operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ora, { Ora } from 'ora';
|
|
7
|
+
|
|
8
|
+
export function startSpinner(text: string): Ora {
|
|
9
|
+
return ora({
|
|
10
|
+
text,
|
|
11
|
+
color: 'cyan',
|
|
12
|
+
}).start();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function stopSpinner(spinner: Ora, success: boolean, text?: string) {
|
|
16
|
+
if (success) {
|
|
17
|
+
spinner.succeed(text || spinner.text);
|
|
18
|
+
} else {
|
|
19
|
+
spinner.fail(text || spinner.text);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function updateSpinner(spinner: Ora, text: string) {
|
|
24
|
+
spinner.text = text;
|
|
25
|
+
}
|