diff --git a/src/backend/exports.js b/src/backend/exports.js index e9a4fa3b1..ef002e174 100644 --- a/src/backend/exports.js +++ b/src/backend/exports.js @@ -63,7 +63,7 @@ module.exports = { HostOSModule, CoreModule, WebModule, - TemplateModule, + // TemplateModule, AppsModule, CaptchaModule, EntityStoreModule, diff --git a/src/backend/src/Extension.js b/src/backend/src/Extension.js index f40a21c46..8e3b9d1ab 100644 --- a/src/backend/src/Extension.js +++ b/src/backend/src/Extension.js @@ -36,6 +36,20 @@ class Extension extends AdvancedBase { ] }), ]; + + randomBrightColor() { + // Bright colors in ANSI (foreground codes 90–97) + const brightColors = [ + // 91, // Bright Red + 92, // Bright Green + // 93, // Bright Yellow + 94, // Bright Blue + 95, // Bright Magenta + // 96, // Bright Cyan + ]; + + return brightColors[Math.floor(Math.random() * brightColors.length)]; + } constructor (...a) { super(...a); @@ -43,6 +57,9 @@ class Extension extends AdvancedBase { this.log = null; this.ensure_service_(); + // this.terminal_color = this.randomBrightColor(); + this.terminal_color = 94; + this.log = (...a) => { this.log_context.info(a.join(' ')); }; @@ -260,6 +277,14 @@ class Extension extends AdvancedBase { } this.only_one_init_fn = callback; } + + get console () { + const extensionConsole = Object.create(console); + extensionConsole.log = (...a) => { + console.log(`\x1B[${this.terminal_color};1m(extension/${this.name})\x1B[0m`, ...a); + }; + return extensionConsole; + } /** * This method will create the "default service" for an extension. diff --git a/src/backend/src/Kernel.js b/src/backend/src/Kernel.js index 0557b28d0..daaa41784 100644 --- a/src/backend/src/Kernel.js +++ b/src/backend/src/Kernel.js @@ -218,8 +218,6 @@ class Kernel extends AdvancedBase { await services.ready; globalThis.services = services; const log = services.get('log-service').create('init'); - log.info('services ready'); - log.system('server ready', { deployment_type: globalThis.deployment_type, }); @@ -411,6 +409,7 @@ class Kernel extends AdvancedBase { `const { use: puter } = globalThis.__puter_extension_globals__.useapi;`, `const extension = globalThis.__puter_extension_globals__` + `.extensionObjectRegistry[${JSON.stringify(extension_id)}];`, + `const console = extension.console;`, `const runtime = extension.runtime;`, `const config = extension.config;`, `const registry = extension.registry;`, @@ -465,6 +464,8 @@ class Kernel extends AdvancedBase { }, }); + mod.extension.name = packageJSON.name; + const maybe_promise = (typ => typ.trim().toLowerCase())(packageJSON.type ?? '') === 'module' ? await import(path_.join(require_dir, packageJSON.main ?? 'index.js')) : require(require_dir); @@ -577,7 +578,7 @@ class Kernel extends AdvancedBase { async run_npm_install (path) { const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm"; - const proc = spawn(npmCmd, ["install"], { cwd: path, shell: true, stdio: "pipe" }); + const proc = spawn(npmCmd, ["install"], { cwd: path, stdio: "pipe" }); let buffer = ''; diff --git a/src/backend/src/boot/BootLogger.js b/src/backend/src/boot/BootLogger.js index 0cad37dc7..4dabe0b8d 100644 --- a/src/backend/src/boot/BootLogger.js +++ b/src/backend/src/boot/BootLogger.js @@ -17,11 +17,6 @@ * along with this program. If not, see . */ class BootLogger { - constructor () { - console.log( - `\x1B[36;1mBoot logger started :)\x1B[0m`, - ); - } info (...args) { console.log( '\x1B[36;1m[BOOT/INFO]\x1B[0m', diff --git a/src/backend/src/boot/RuntimeEnvironment.js b/src/backend/src/boot/RuntimeEnvironment.js index be547202d..3d05ea842 100644 --- a/src/backend/src/boot/RuntimeEnvironment.js +++ b/src/backend/src/boot/RuntimeEnvironment.js @@ -381,7 +381,7 @@ class RuntimeEnvironment extends AdvancedBase { if ( ! checks_pass ) continue; this.logger.info( - `${hl('USING')} ${quot(entry.path)} for ${meta.pathFor}.` + `${hl(meta.pathFor)} ${quot(entry.path)}` ) return entry; diff --git a/src/backend/src/modules/broadcast/BroadcastService.js b/src/backend/src/modules/broadcast/BroadcastService.js index 5b7a7cbfa..bd9bfdabe 100644 --- a/src/backend/src/modules/broadcast/BroadcastService.js +++ b/src/backend/src/modules/broadcast/BroadcastService.js @@ -102,11 +102,6 @@ class BroadcastService extends BaseService { }); }); }); - - - this.log.noticeme( - require('node:util').inspect(this.config) - ); } _register_commands (commands) { diff --git a/src/backend/src/modules/selfhosted/ComplainAboutVersionsService.js b/src/backend/src/modules/selfhosted/ComplainAboutVersionsService.js index d28957666..ed0011a21 100644 --- a/src/backend/src/modules/selfhosted/ComplainAboutVersionsService.js +++ b/src/backend/src/modules/selfhosted/ComplainAboutVersionsService.js @@ -54,7 +54,7 @@ class ComplainAboutVersionsService extends BaseService { const cur_date_obj = new Date(); if ( cur_date_obj < eol_date ) { - this.log.info('node.js version looks good'); + this.log.debug('node.js version looks good'); return; } @@ -75,22 +75,13 @@ class ComplainAboutVersionsService extends BaseService { return str; })(); - const svc_devConsole = this.services.get('dev-console'); - svc_devConsole.add_widget(() => { - const widget_lines = []; - widget_lines.push( - `Node.js version ${major} is past EOL by ${timeago};`, - `Everything should work, but you should still upgrade.`, - ); - surrounding_box('31;1', widget_lines); - return widget_lines; - }); + this.log.warn(`Node.js version ${major} is past EOL by ${timeago}`); } async get_eol_data_ () { const require = this.require; const axios = require('axios'); - const url = 'https://endoflife.date/api/nodejs.json' + const url = 'https://endoflife.date/api/nodejs.json'; let data; try { ({ data } = await axios.get(url)); diff --git a/src/backend/src/modules/selfhosted/DevWatcherService.js b/src/backend/src/modules/selfhosted/DevWatcherService.js index f3623200a..a3fac7c18 100644 --- a/src/backend/src/modules/selfhosted/DevWatcherService.js +++ b/src/backend/src/modules/selfhosted/DevWatcherService.js @@ -176,7 +176,14 @@ class DevWatcherService extends BaseService { const webpacker = webpack(webpackConfig); + let errorAfterLastEnd = false; + let firstEvent = true; webpacker.watch({}, (err, stats) => { + let hideSuccess = false; + if ( firstEvent ) { + firstEvent = false; + hideSuccess = true; + } if (err || stats.hasErrors()) { this.log.error(`error information: ${entry.directory} using Webpack`, { err, @@ -187,7 +194,9 @@ class DevWatcherService extends BaseService { // Normally success messages aren't important, but sometimes it takes // a little bit for the bundle to update so a developer probably would // like to have a visual indication in the console when it happens. - this.log.info(`✅ updated ${entry.directory} using Webpack`); + if ( ! hideSuccess ) { + this.log.info(`✅ updated ${entry.directory} using Webpack`); + } } }); } @@ -244,13 +253,21 @@ class DevWatcherService extends BaseService { const watcher = rollupModule.watch(rollupConfig); let errorAfterLastEnd = false; + let firstEvent = true; watcher.on('event', (event) => { if ( event.code === 'END' ) { + let hideSuccess = false; + if ( firstEvent ) { + firstEvent = false; + hideSuccess = true; + } if ( errorAfterLastEnd ) { errorAfterLastEnd = false; return; } - this.log.info(`✅ updated ${entry.directory} using Rollup`); + if ( ! hideSuccess ) { + this.log.info(`✅ updated ${entry.directory} using Rollup`); + } } else if ( event.code === 'ERROR' ) { this.log.error(`error information: ${entry.directory} using Rollup`, { event, diff --git a/src/backend/src/modules/selfhosted/SelfHostedModule.js b/src/backend/src/modules/selfhosted/SelfHostedModule.js index 9fc7f5271..4273f52b1 100644 --- a/src/backend/src/modules/selfhosted/SelfHostedModule.js +++ b/src/backend/src/modules/selfhosted/SelfHostedModule.js @@ -41,8 +41,8 @@ class SelfHostedModule extends AdvancedBase { const { DBKVServiceWrapper } = require("../../services/repositories/DBKVStore/index.mjs"); services.registerService('puter-kvstore', DBKVServiceWrapper); - const MinLogService = require('./MinLogService'); - services.registerService('min-log', MinLogService); + // const MinLogService = require('./MinLogService'); + // services.registerService('min-log', MinLogService); // TODO: sucks const RELATIVE_PATH = '../../../../../'; diff --git a/src/backend/src/modules/web/WebServerService.js b/src/backend/src/modules/web/WebServerService.js index 5a06322b1..4f8290876 100644 --- a/src/backend/src/modules/web/WebServerService.js +++ b/src/backend/src/modules/web/WebServerService.js @@ -117,7 +117,7 @@ class WebServerService extends BaseService { const services = this.services; await services.emit('start.webserver'); await services.emit('ready.webserver'); - this.print_puter_logo_(); + // this.print_puter_logo_(); } @@ -223,21 +223,13 @@ class WebServerService extends BaseService { console.log('Error opening browser', e); } } - /** - * Starts the HTTP server. - * - * This method sets up the Express server, initializes middleware, and starts the HTTP server. - * It handles error handling, authentication, and other necessary configurations. - * - * @returns {Promise} A Promise that resolves when the server is listening. - */ + + const link = `\x1B[34;1m${strutil.osclink(url)}\x1B[0m`; + const lines = [ + `Puter is now live at: ${link}`, + ]; this.startup_widget = () => { - const link = `\x1B[34;1m${strutil.osclink(url)}\x1B[0m`; - const lines = [ - `Puter is now live at: ${link}`, - `Type web:dismiss to un-stick this message`, - ]; const lengths = [ (`Puter is now live at: `).length + url.length, lines[1].length, @@ -245,9 +237,16 @@ class WebServerService extends BaseService { surrounding_box('34;1', lines, lengths); return lines; }; - { + if ( this.config.old_widget_behavior ) { const svc_devConsole = this.services.get('dev-console', { optional: true }); if ( svc_devConsole ) svc_devConsole.add_widget(this.startup_widget); + } else { + const svc_devConsole = this.services.get('dev-console', { optional: true }); + svc_devConsole.notice({ + colors: { bg: '38;2;0;0;0;48;2;0;202;252;1', bginv: '38;2;0;202;252' }, + title: 'Puter is live!', + lines, + }); } server.timeout = 1000 * 60 * 60 * 2; // 2 hours diff --git a/src/backend/src/services/BaseService.js b/src/backend/src/services/BaseService.js index 9b1cc225c..36c572019 100644 --- a/src/backend/src/services/BaseService.js +++ b/src/backend/src/services/BaseService.js @@ -50,7 +50,9 @@ class BaseService extends concepts.Service { Object.defineProperty(this, 'config', { get: () => configOverride ?? config.services?.[name] ?? {}, set: why => { - console.warn('replacing config like this is probably a bad idea'); + // TODO: uncomment and fix these in legacy services + // (not very important; low priority) + // console.warn('replacing config like this is probably a bad idea'); configOverride = why; }, }); diff --git a/src/backend/src/services/DevConsoleService.js b/src/backend/src/services/DevConsoleService.js index 24c532078..cd9898dc4 100644 --- a/src/backend/src/services/DevConsoleService.js +++ b/src/backend/src/services/DevConsoleService.js @@ -90,7 +90,18 @@ class DevConsoleService extends BaseService { this.widgets = this.widgets.filter(w => w !== id_or_outputter); this.mark_updated(); } + + notice ({ colors, title, lines }) { + colors = colors ?? { + bg: '46', + bginv: '36', + }; + console.log(`\x1B[${colors.bginv}m▐\x1B[0m\x1B[${colors.bg}m ${title} \x1B[0m`); + for ( const line of lines ) { + console.log(`\x1B[${colors.bginv}m▐▌\x1B[0m${line}\x1B[0m`); + } + } /** * Updates the displayed output based on the current state of widgets. @@ -142,7 +153,7 @@ class DevConsoleService extends BaseService { for ( let i = this.widgets.length-1 ; i >= 0 ; i-- ) { if ( size_ok() ) break; const w = this.widgets[i]; - if ( w.critical ) continue; + if ( w.critical ) continue; n_hidden++; const [start, length] = positions[i]; this.static_lines.splice(start, length); diff --git a/src/backend/src/services/DevTODService.js b/src/backend/src/services/DevTODService.js index f4e799c55..6f24ecf5c 100644 --- a/src/backend/src/services/DevTODService.js +++ b/src/backend/src/services/DevTODService.js @@ -17,6 +17,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +const putility = require("@heyputer/putility"); const { surrounding_box } = require("../fun/dev-console-ui-utils"); const BaseService = require("./BaseService"); @@ -88,28 +89,39 @@ class DevTODService extends BaseService { */ async ['__on_boot.consolidation'] () { let random_tip = tips[Math.floor(Math.random() * tips.length)]; - random_tip = wordwrap( - random_tip, - process.stdout.columns - ? process.stdout.columns - 6 : 50 - ); - this.tod_widget = () => { - const lines = [ - ...random_tip, - ]; - if ( ! this.global_config.minimal_console ) { - lines.unshift("\x1B[1mTip of the Day\x1B[0m"); - lines.push("Type tod:dismiss to un-stick this message"); - } - surrounding_box('33;1', lines); - return lines; + if ( this.config.old_widget_behavior ) { + random_tip = wordwrap( + random_tip, + process.stdout.columns + ? process.stdout.columns - 6 : 50, + ); + + this.tod_widget = () => { + const lines = [ + ...random_tip, + ]; + if ( ! this.global_config.minimal_console ) { + lines.unshift("\x1B[1mTip of the Day\x1B[0m"); + lines.push("Type tod:dismiss to un-stick this message"); + } + surrounding_box('33;1', lines); + return lines; + }; + + this.tod_widget.unimportant = true; + + const svc_devConsole = this.services.get('dev-console', { optional: true }); + if ( ! svc_devConsole ) return; + svc_devConsole.add_widget(this.tod_widget); + } else { + const svc_devConsole = this.services.get('dev-console', { optional: true }); + if ( ! svc_devConsole ) return; + svc_devConsole.notice({ + colors: { bg: '38;2;0;0;0;48;2;255;255;0;1', bginv: '38;2;255;255;0' }, + title: 'Tip of the Day', + lines: putility.libs.string.wrap_text(random_tip), + }); } - - this.tod_widget.unimportant = true; - - const svc_devConsole = this.services.get('dev-console', { optional: true }); - if ( ! svc_devConsole ) return; - svc_devConsole.add_widget(this.tod_widget); } _register_commands (commands) { diff --git a/src/putility/src/libs/string.js b/src/putility/src/libs/string.js index 8e85524aa..9f8d2a6f7 100644 --- a/src/putility/src/libs/string.js +++ b/src/putility/src/libs/string.js @@ -68,9 +68,40 @@ const format_as_usd = (amount) => { return '$' + amount.toFixed(2); } +// wrap.js +const wrap_text = (text, width = 71) => { + const out = []; + const paras = text.split(/\r?\n\s*\r?\n/); // split on blank lines + + for (const p of paras) { + const words = p.trim().replace(/\s+/g, ' ').split(' '); + if (words.length === 1 && words[0] === '') { out.push(''); continue; } + + let line = ''; + for (const w of words) { + if (line.length === 0) { + // start new line; do NOT split long words + line = w; + } else if (line.length + 1 + w.length <= width) { + line += ' ' + w; + } else { + out.push(line); + line = w; // put word on its own new line (even if > width) + } + } + if (line) out.push(line); + out.push(''); // blank line between paragraphs + } + + // remove the extra trailing blank line + if (out.length && out[out.length - 1] === '') out.pop(); + + return out; +}; + module.exports = { quot, osclink, format_as_usd, + wrap_text, }; -