superjolt
Version:
AI-powered deployment platform with MCP support - Deploy JavaScript apps using natural language with Claude Desktop
172 lines • 7.6 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogsCommand = void 0;
const nest_commander_1 = require("nest-commander");
const authenticated_command_1 = require("./authenticated.command");
const api_service_1 = require("../services/api.service");
const common_1 = require("@nestjs/common");
const config_service_1 = require("../services/config.service");
const auth_service_1 = require("../services/auth.service");
const logger_service_1 = require("../services/logger.service");
const project_1 = require("../utils/project");
let LogsCommand = class LogsCommand extends authenticated_command_1.AuthenticatedCommand {
apiService;
configService;
authService;
logger;
constructor(apiService, configService, authService, logger) {
super();
this.apiService = apiService;
this.configService = configService;
this.authService = authService;
this.logger = logger;
}
async execute(passedParams, options) {
try {
let serviceId = passedParams[0];
if (!serviceId) {
const config = (0, project_1.readSuperjoltConfig)();
if (config?.serviceId) {
serviceId = config.serviceId;
this.logger.log(`Using service ID from .superjolt file: ${serviceId}`);
}
else {
this.logger.error('Error: Service ID is required');
this.logger.log('Usage: superjolt logs [options] <serviceId>');
this.logger.log('\nNo .superjolt file found. Run "superjolt deploy" first or provide a service ID.');
process.exit(1);
}
}
if (options.follow) {
await this.followLogs(serviceId, options.tail || 20);
}
else {
await this.getStaticLogs(serviceId, options.tail || 20);
}
}
catch (error) {
this.logger.error(`\n${error.message}`);
process.exit(1);
}
}
parseFollow() {
return true;
}
parseTail(val) {
const num = parseInt(val, 10);
if (isNaN(num) || num < 1) {
throw new Error('Tail value must be a positive number');
}
return num;
}
async getStaticLogs(serviceId, tail) {
this.logger.log(`Fetching last ${tail} lines for service: ${serviceId}...`);
this.logger.log('');
const response = await this.apiService.getServiceLogs(serviceId, { tail });
this.logger.log(response.logs);
if (response.metadata) {
this.logger.log('');
this.logger.log('─'.repeat(80));
this.logger.log(`Lines: ${response.metadata.lines}${response.metadata.truncated ? ' (truncated)' : ''}`);
}
}
async followLogs(serviceId, tail) {
const { EventSource } = require('eventsource');
const chalkModule = require('chalk');
const chalk = chalkModule.default || chalkModule;
this.logger.log(`Following logs for service: ${serviceId} (showing last ${tail} lines + new logs)...`);
this.logger.log('Press Ctrl+C to stop following');
this.logger.log('');
const apiUrl = this.configService.getApiUrl();
const token = await this.authService.getToken();
const streamUrl = `${apiUrl}/service/${serviceId}/logs/stream?tail=${tail}&token=${encodeURIComponent(token || '')}`;
const eventSource = new EventSource(streamUrl);
return new Promise((resolve, reject) => {
eventSource.onmessage = (event) => {
try {
const logEvent = JSON.parse(event.data);
switch (logEvent.type) {
case 'connected':
this.logger.log(chalk.blue('📡 Connected to log stream'));
this.logger.log('');
break;
case 'log':
if (logEvent.data) {
process.stdout.write(logEvent.data);
}
break;
case 'error':
this.logger.error(chalk.red(`❌ Log stream error: ${logEvent.error}`));
eventSource.close();
reject(new Error(logEvent.error));
break;
case 'end':
this.logger.log('');
this.logger.log(chalk.yellow('📡 Log stream ended'));
eventSource.close();
resolve();
break;
default:
this.logger.log(chalk.gray(`[${logEvent.type}] ${logEvent.message || JSON.stringify(logEvent)}`));
}
}
catch {
this.logger.error('Failed to parse log event:', event.data);
}
};
eventSource.onerror = () => {
this.logger.error('');
this.logger.error(chalk.red('❌ Connection to log stream failed'));
eventSource.close();
reject(new Error('Log stream connection failed'));
};
process.on('SIGINT', () => {
this.logger.log('');
this.logger.log(chalk.yellow('📡 Stopping log stream...'));
eventSource.close();
resolve();
});
});
}
};
exports.LogsCommand = LogsCommand;
__decorate([
(0, nest_commander_1.Option)({
flags: '-f, --follow',
description: 'Follow log output (like tail -f)',
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Boolean)
], LogsCommand.prototype, "parseFollow", null);
__decorate([
(0, nest_commander_1.Option)({
flags: '-n, --tail <lines>',
description: 'Number of lines to show from the end of the logs (default: 20)',
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Number)
], LogsCommand.prototype, "parseTail", null);
exports.LogsCommand = LogsCommand = __decorate([
(0, common_1.Injectable)(),
(0, nest_commander_1.Command)({
name: 'logs',
arguments: '[serviceId]',
description: 'Get logs for a service',
}),
__metadata("design:paramtypes", [api_service_1.ApiService,
config_service_1.ConfigService,
auth_service_1.AuthService,
logger_service_1.LoggerService])
], LogsCommand);
//# sourceMappingURL=logs.command.js.map