mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-30 17:50:00 -06:00
@@ -25,6 +25,6 @@ extension.on('init', async () => {
|
||||
// and just a little bit longer
|
||||
// await sleep(100);
|
||||
|
||||
console.log('kv key should no longer have the value', kv.get('example-kv-key'));
|
||||
console.log('kv key should no longer have the value', await kv.get('example-kv-key'));
|
||||
})();
|
||||
});
|
||||
|
||||
5543
package-lock.json
generated
5543
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.879.0",
|
||||
"@aws-sdk/client-sns": "^3.907.0",
|
||||
"@google/genai": "^1.19.0",
|
||||
"@heyputer/putility": "^1.0.2",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
@@ -74,11 +75,11 @@
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sharp": "^0.34.3",
|
||||
"sharp": "^0.34.4",
|
||||
"sharp-bmp": "^0.1.5",
|
||||
"sharp-ico": "^0.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ module.exports = {
|
||||
HostOSModule,
|
||||
CoreModule,
|
||||
WebModule,
|
||||
TemplateModule,
|
||||
// TemplateModule,
|
||||
AppsModule,
|
||||
CaptchaModule,
|
||||
EntityStoreModule,
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
"@pagerduty/pdjs": "^2.2.4",
|
||||
"@smithy/node-http-handler": "^2.2.2",
|
||||
"args": "^5.0.3",
|
||||
"aws-sdk": "^2.1383.0",
|
||||
"axios": "^1.8.2",
|
||||
"bcrypt": "^5.1.0",
|
||||
"better-sqlite3": "^11.9.0",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,18 +578,34 @@ 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: "inherit" });
|
||||
const proc = spawn(npmCmd, ["install"], { cwd: path, stdio: "pipe" });
|
||||
|
||||
let buffer = '';
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
buffer += data.toString();
|
||||
});
|
||||
|
||||
proc.stderr.on('data', (data) => {
|
||||
buffer += data.toString();
|
||||
});
|
||||
|
||||
return new Promise((rslv, rjct) => {
|
||||
proc.on('close', code => {
|
||||
if ( code !== 0 ) {
|
||||
throw new Error(`exit code: ${code}`);
|
||||
// Print buffered output on error
|
||||
if ( buffer ) process.stdout.write(buffer);
|
||||
rjct(new Error(`exit code: ${code}`));
|
||||
return;
|
||||
}
|
||||
rslv();
|
||||
});
|
||||
proc.on('error', err => {
|
||||
// Print buffered output on error
|
||||
if ( buffer ) process.stdout.write(buffer);
|
||||
rjct(err);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,11 +17,6 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
class BootLogger {
|
||||
constructor () {
|
||||
console.log(
|
||||
`\x1B[36;1mBoot logger started :)\x1B[0m`,
|
||||
);
|
||||
}
|
||||
info (...args) {
|
||||
console.log(
|
||||
'\x1B[36;1m[BOOT/INFO]\x1B[0m',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -246,7 +246,8 @@ const config_pointer = {};
|
||||
config_to_export = new Proxy(config_to_export, {
|
||||
set: (target, prop, value, receiver) => {
|
||||
const logger = Context.get('logger', { allow_fallback: true });
|
||||
logger?.debug(
|
||||
// If no logger, just give up
|
||||
if ( logger ) logger.debug(
|
||||
'\x1B[36;1mCONFIGURATION MUTATED AT RUNTIME\x1B[0m',
|
||||
{ prop, value },
|
||||
);
|
||||
|
||||
@@ -323,7 +323,7 @@ module.exports = class FSNodeContext {
|
||||
},
|
||||
};
|
||||
|
||||
this.log.info('fetching entry: ' + this.selector.describe());
|
||||
this.log.debug('fetching entry: ' + this.selector.describe());
|
||||
|
||||
const entry = await this.provider.stat({
|
||||
selector: this.selector,
|
||||
|
||||
@@ -64,7 +64,7 @@ class HLReadDir extends HLFilesystemOperation {
|
||||
|
||||
let children;
|
||||
|
||||
this.log.noticeme('READDIR',
|
||||
this.log.debug('READDIR',
|
||||
{
|
||||
userdir: await subject.isUserDirectory(),
|
||||
namediff: await subject.get('name') !== user.username
|
||||
|
||||
@@ -102,11 +102,6 @@ class BroadcastService extends BaseService {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.log.noticeme(
|
||||
require('node:util').inspect(this.config)
|
||||
);
|
||||
}
|
||||
|
||||
_register_commands (commands) {
|
||||
|
||||
@@ -71,7 +71,7 @@ class CaptchaService extends BaseService {
|
||||
*/
|
||||
async _init() {
|
||||
if (!this.enabled) {
|
||||
this.log.info('Captcha service is disabled');
|
||||
this.log.debug('Captcha service is disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ class CaptchaService extends BaseService {
|
||||
});
|
||||
|
||||
this.endpointsRegistered = true;
|
||||
this.log.info('Captcha service endpoints registered successfully');
|
||||
this.log.debug('Captcha service endpoints registered successfully');
|
||||
|
||||
// Emit an event that captcha service is ready
|
||||
try {
|
||||
|
||||
@@ -524,7 +524,8 @@ class LogService extends BaseService {
|
||||
}
|
||||
|
||||
this.log = this.create('log-service');
|
||||
this.log.system('log service started', {
|
||||
this.log.system('log service started');
|
||||
this.log.debug('log service configuration', {
|
||||
output_lvl: this.output_lvl,
|
||||
log_directory: this.log_directory,
|
||||
});
|
||||
|
||||
@@ -106,7 +106,7 @@ class ServerHealthService extends BaseService {
|
||||
mem_total: meminfo.MemTotal,
|
||||
};
|
||||
|
||||
this.log.info('memory', log_fields);
|
||||
this.log.debug('memory', log_fields);
|
||||
|
||||
Object.assign(this.stats_, log_fields);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class DNSService extends BaseService {
|
||||
});
|
||||
|
||||
server.on('listening', () => {
|
||||
this.log.info('Fake DNS server listening', server.addresses());
|
||||
this.log.debug('Fake DNS server listening', server.addresses());
|
||||
|
||||
if ( this.config.test_server_selftest ) (async () => {
|
||||
await sleep(5000);
|
||||
|
||||
@@ -192,7 +192,7 @@ module.exports = class DatabaseFSEntryFetcher extends BaseService {
|
||||
|
||||
if ( result[0] ) return result[0];
|
||||
|
||||
this.log.info(`findByPath (not cached): ${path}`)
|
||||
this.log.debug(`findByPath (not cached): ${path}`)
|
||||
|
||||
const loop = async () => {
|
||||
for ( let i=0 ; i < parts.length ; i++ ) {
|
||||
|
||||
@@ -457,7 +457,7 @@ class DatabaseFSEntryService extends BaseService {
|
||||
|
||||
const queue = this.currentState.queue;
|
||||
|
||||
this.log.info(
|
||||
this.log.debug(
|
||||
`Executing ${queue.length} operations...`
|
||||
);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class ResourceService extends BaseService {
|
||||
};
|
||||
});
|
||||
entry.onFree = entry.freePromise.then.bind(entry.freePromise);
|
||||
this.log.info(`registering resource`, { uid: entry.uid });
|
||||
this.log.debug(`registering resource`, { uid: entry.uid });
|
||||
this.uidToEntry[entry.uid] = entry;
|
||||
if ( entry.path ) {
|
||||
this.uidToPath[entry.uid] = entry.path;
|
||||
@@ -72,7 +72,7 @@ class ResourceService extends BaseService {
|
||||
}
|
||||
|
||||
free (uid) {
|
||||
this.log.info(`freeing`, { uid });
|
||||
this.log.debug(`freeing`, { uid });
|
||||
const entry = this.uidToEntry[uid];
|
||||
if ( ! entry ) return;
|
||||
delete this.uidToEntry[uid];
|
||||
|
||||
@@ -185,7 +185,7 @@ class PuterFSProvider extends putility.AdvancedBase {
|
||||
}
|
||||
|
||||
if ( ! entry ) {
|
||||
controls.log.info(`entry not found: ${selector.describe(true)}`);
|
||||
controls.log.warn(`entry not found: ${selector.describe(true)}`);
|
||||
}
|
||||
|
||||
if ( entry === null || typeof entry !== 'object' ) {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const { webpack, web } = require("webpack");
|
||||
const BaseService = require("../../services/BaseService");
|
||||
|
||||
const path_ = require('node:path');
|
||||
const fs = require('node:fs');
|
||||
const rollupModule = require("rollup");
|
||||
|
||||
class ProxyLogger {
|
||||
constructor (log) {
|
||||
this.log = log;
|
||||
@@ -65,7 +70,10 @@ class DevWatcherService extends BaseService {
|
||||
async ['__on_ready.webserver'] () {
|
||||
const svc_process = this.services.get('process');
|
||||
|
||||
const { root, commands } = this.args;
|
||||
let { root, commands, webpack, rollup } = this.args;
|
||||
if ( ! webpack ) webpack = [];
|
||||
if ( ! rollup ) rollup = [];
|
||||
|
||||
let promises = [];
|
||||
for ( const entry of commands ) {
|
||||
const { directory } = entry;
|
||||
@@ -74,12 +82,201 @@ class DevWatcherService extends BaseService {
|
||||
// promises.push(this.start_({ ...entry, fullpath }));
|
||||
promises.push(svc_process.start({ ...entry, fullpath }));
|
||||
}
|
||||
for ( const entry of webpack ) {
|
||||
const p = this.start_a_webpack_watcher_(entry);
|
||||
promises.push(p);
|
||||
}
|
||||
for ( const entry of rollup ) {
|
||||
const p = this.start_a_rollup_watcher_(entry);
|
||||
promises.push(p);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
// It's difficult to tell when webpack is "done" its first
|
||||
// run so we just wait a bit before we say we're ready.
|
||||
await new Promise((resolve) => setTimeout(resolve, 5000));
|
||||
}
|
||||
|
||||
async get_configjs ({ directory, configIsFor, possibleConfigNames }) {
|
||||
let configjsPath, moduleType;
|
||||
|
||||
for ( const [configName, supposedModuleType] of possibleConfigNames ) {
|
||||
// There isn't really an async fs.exists() funciton. I assume this
|
||||
// is because 'exists' is already a very fast operation.
|
||||
const supposedPath = path_.join(this.args.root, directory, configName);
|
||||
if ( fs.existsSync(supposedPath) ) {
|
||||
configjsPath = supposedPath;
|
||||
moduleType = supposedModuleType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! configjsPath ) {
|
||||
throw new Error(`could not find ${configIsFor} config for: ${directory}`);
|
||||
}
|
||||
|
||||
// If the webpack config ends with .js it could be an ES6 module or a
|
||||
// CJS module, so the absolute safest thing to do so as not to completely
|
||||
// break in specific patch version of supported versions of node.js is
|
||||
// to read the package.json and see what it says is the import mechanism.
|
||||
if ( moduleType === 'package.json' ) {
|
||||
const packageJSONPath = path_.join(this.args.root, directory, 'package.json');
|
||||
const packageJSONObject = JSON.parse(fs.readFileSync(packageJSONPath));
|
||||
moduleType = packageJSONObject?.type ?? 'module';
|
||||
}
|
||||
|
||||
return {
|
||||
configjsPath,
|
||||
moduleType,
|
||||
};
|
||||
}
|
||||
|
||||
async start_a_webpack_watcher_ (entry) {
|
||||
const possibleConfigNames = [
|
||||
['webpack.config.js', 'package.json'],
|
||||
['webpack.config.cjs', 'commonjs'],
|
||||
['webpack.config.mjs', 'module'],
|
||||
];
|
||||
|
||||
const {
|
||||
configjsPath: webpackConfigPath,
|
||||
moduleType,
|
||||
} = await this.get_configjs({
|
||||
directory: entry.directory,
|
||||
configIsFor: 'webpack', // for error message
|
||||
possibleConfigNames,
|
||||
});
|
||||
|
||||
let oldEnv;
|
||||
|
||||
if ( entry.env ) {
|
||||
oldEnv = process.env;
|
||||
const newEnv = Object.create(process.env);
|
||||
for ( const k in entry.env ) {
|
||||
newEnv[k] = entry.env[k];
|
||||
}
|
||||
process.env = newEnv; // Yep, it totally lets us do this
|
||||
}
|
||||
let webpackConfig = moduleType === 'module'
|
||||
? (await import(webpackConfigPath)).default
|
||||
: require(webpackConfigPath);
|
||||
|
||||
// The webpack config can sometimes be a function
|
||||
if ( typeof webpackConfig === 'function' ) {
|
||||
webpackConfig = await webpackConfig();
|
||||
}
|
||||
|
||||
if ( oldEnv ) process.env = oldEnv;
|
||||
|
||||
webpackConfig.context = webpackConfig.context
|
||||
? path_.resolve(path_.join(this.args.root, entry.directory), webpackConfig.context)
|
||||
: path_.join(this.args.root, entry.directory);
|
||||
|
||||
if ( entry.onConfig ) entry.onConfig(webpackConfig);
|
||||
|
||||
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,
|
||||
stats,
|
||||
});
|
||||
this.log.error(`❌ failed to update ${entry.directory} using Webpack`);
|
||||
} else {
|
||||
// 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.
|
||||
if ( ! hideSuccess ) {
|
||||
this.log.info(`✅ updated ${entry.directory} using Webpack`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async start_a_rollup_watcher_ (entry) {
|
||||
const possibleConfigNames = [
|
||||
['rollup.config.js', 'package.json'],
|
||||
['rollup.config.cjs', 'commonjs'],
|
||||
['rollup.config.mjs', 'module'],
|
||||
];
|
||||
|
||||
const {
|
||||
configjsPath: rollupConfigPath,
|
||||
moduleType,
|
||||
} = await this.get_configjs({
|
||||
directory: entry.directory,
|
||||
configIsFor: 'rollup', // for error message
|
||||
possibleConfigNames,
|
||||
});
|
||||
|
||||
const updateRollupPaths = (config, newBase) => {
|
||||
const onoutput = o => ({ ...o, file: o.file ? path_.join(newBase, o.file) : o.file });
|
||||
return {
|
||||
...config,
|
||||
input: path_.join(newBase, config.input),
|
||||
output: Array.isArray(config.output)
|
||||
? config.output.map(onoutput)
|
||||
: onoutput(config.output),
|
||||
};
|
||||
};
|
||||
|
||||
let oldEnv;
|
||||
|
||||
if ( entry.env ) {
|
||||
oldEnv = process.env;
|
||||
const newEnv = Object.create(process.env);
|
||||
for ( const k in entry.env ) {
|
||||
newEnv[k] = entry.env[k];
|
||||
}
|
||||
process.env = newEnv; // Yep, it totally lets us do this
|
||||
}
|
||||
|
||||
let rollupConfig = moduleType === 'module'
|
||||
? (await import(rollupConfigPath)).default
|
||||
: require(rollupConfigPath);
|
||||
|
||||
if ( oldEnv ) process.env = oldEnv;
|
||||
|
||||
rollupConfig = updateRollupPaths(
|
||||
rollupConfig,
|
||||
path_.join(this.args.root, entry.directory),
|
||||
);
|
||||
// rollupConfig.watch = true; // I mean why can't it just...
|
||||
|
||||
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;
|
||||
}
|
||||
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,
|
||||
});
|
||||
this.log.error(`❌ failed to update ${entry.directory} using Rollup`);
|
||||
errorAfterLastEnd = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DevWatcherService;
|
||||
|
||||
@@ -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 = '../../../../../';
|
||||
@@ -51,48 +51,46 @@ class SelfHostedModule extends AdvancedBase {
|
||||
{
|
||||
services.registerService('__dev-watcher', DevWatcherService, {
|
||||
root: path_.resolve(__dirname, RELATIVE_PATH),
|
||||
commands: [
|
||||
rollup: [
|
||||
{
|
||||
name: 'puter.js:webpack-watch',
|
||||
name: 'phoenix',
|
||||
directory: 'src/phoenix',
|
||||
env: {
|
||||
PUTER_JS_URL: ({ global_config: config }) => config.origin + '/sdk/puter.dev.js',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'terminal',
|
||||
directory: 'src/terminal',
|
||||
env: {
|
||||
PUTER_JS_URL: ({ global_config: config }) => config.origin + '/sdk/puter.dev.js',
|
||||
},
|
||||
},
|
||||
],
|
||||
webpack: [
|
||||
{
|
||||
name: 'puter.js',
|
||||
directory: 'src/puter-js',
|
||||
command: 'npm',
|
||||
args: ['run', 'start-webpack'],
|
||||
onConfig: config => {
|
||||
config.output.filename = 'puter.dev.js';
|
||||
config.devtool = 'source-map';
|
||||
},
|
||||
env: {
|
||||
PUTER_ORIGIN: ({ global_config: config }) => config.origin,
|
||||
PUTER_API_ORIGIN: ({ global_config: config }) => config.api_base_url,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'gui:webpack-watch',
|
||||
name: 'gui',
|
||||
directory: 'src/gui',
|
||||
command: 'npm',
|
||||
args: ['run', 'start-webpack'],
|
||||
},
|
||||
{
|
||||
name: 'terminal:rollup-watch',
|
||||
directory: 'src/terminal',
|
||||
command: 'npx',
|
||||
args: ['rollup', '-c', 'rollup.config.js', '--watch'],
|
||||
env: {
|
||||
PUTER_JS_URL: ({ global_config: config }) => config.origin + '/sdk/puter.dev.js',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'phoenix:rollup-watch',
|
||||
directory: 'src/phoenix',
|
||||
command: 'npx',
|
||||
args: ['rollup', '-c', 'rollup.config.js', '--watch'],
|
||||
env: {
|
||||
PUTER_JS_URL: ({ global_config: config }) => config.origin + '/sdk/puter.dev.js',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'emulator:webpack-watch',
|
||||
name: 'emulator',
|
||||
directory: 'src/emulator',
|
||||
command: 'npm',
|
||||
args: ['run', 'start-webpack'],
|
||||
},
|
||||
],
|
||||
commands: [
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ const BaseService = require("../../services/BaseService");
|
||||
|
||||
class TestConfigReadService extends BaseService {
|
||||
async _init () {
|
||||
this.log.noticeme('test config value (should be abcdefg) is: ' +
|
||||
this.log.debug('test config value (should be abcdefg) is: ' +
|
||||
this.global_config.testConfigValue,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ class WebServerService extends BaseService {
|
||||
return res.sendStatus(200);
|
||||
});
|
||||
|
||||
this.log.noticeme('web server setup done');
|
||||
this.log.debug('web server setup done');
|
||||
}
|
||||
|
||||
install_post_middlewares_ ({ app }) {
|
||||
@@ -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_();
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ class WebServerService extends BaseService {
|
||||
for ( let i = 0 ; i < ports_to_try.length ; i++ ) {
|
||||
const port = ports_to_try[i];
|
||||
const is_last_port = i === ports_to_try.length - 1;
|
||||
if ( auto_port ) this.log.info('trying port: ' + port);
|
||||
if ( auto_port ) this.log.debug('trying port: ' + port);
|
||||
try {
|
||||
server = http.createServer(this.app).listen(port);
|
||||
server.timeout = 1000 * 60 * 60 * 2; // 2 hours
|
||||
@@ -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
|
||||
@@ -391,12 +390,11 @@ class WebServerService extends BaseService {
|
||||
fields.status, fields.responseTime,
|
||||
].join(' ');
|
||||
|
||||
const log = this.services.get('log-service').create('morgan', {
|
||||
concern: 'web'
|
||||
});
|
||||
const log = this.services.get('log-service').create('morgan');
|
||||
try {
|
||||
this.context.arun(() => {
|
||||
log.info(message, fields);
|
||||
log.info(message);
|
||||
log.debug(message, fields);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('failed to log this message properly:', message, fields);
|
||||
|
||||
@@ -187,7 +187,7 @@ module.exports = function eggspress (route, settings, handler) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
if ( config.env === 'dev' ) {
|
||||
if ( config.env === 'dev' && process.env.DEBUG ) {
|
||||
console.log(`request url: ${req.url}, body: ${JSON.stringify(req.body)}`);
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -113,7 +113,9 @@ class PropType extends AdvancedBase {
|
||||
this.chains.factory && [...this.chains.factory].reverse()
|
||||
) || [];
|
||||
|
||||
console.log('FACTORIES', factories)
|
||||
if ( process.env.DEBUG ) {
|
||||
console.log('FACTORIES', factories);
|
||||
}
|
||||
|
||||
for ( const factory of factories ) {
|
||||
const result = await factory(extra);
|
||||
|
||||
@@ -49,7 +49,7 @@ module.exports = eggspress('/readdir', {
|
||||
log = x.get('services').get('log-service').create('readdir', {
|
||||
concern: 'filesystem',
|
||||
});
|
||||
log.info(`readdir: ${req.body.subject || req.body.path || req.body.uid}`);
|
||||
log.debug(`readdir: ${req.body.subject || req.body.path || req.body.uid}`);
|
||||
}
|
||||
|
||||
const subject = req.values.subject;
|
||||
|
||||
@@ -59,13 +59,15 @@ const complete_ = async ({ req, res, user }) => {
|
||||
router.post('/login', express.json(), body_parser_error_handler,
|
||||
// Add diagnostic middleware to log captcha data
|
||||
(req, res, next) => {
|
||||
console.log('====== LOGIN CAPTCHA DIAGNOSTIC ======');
|
||||
console.log('LOGIN REQUEST RECEIVED with captcha data:', {
|
||||
hasCaptchaToken: !!req.body.captchaToken,
|
||||
hasCaptchaAnswer: !!req.body.captchaAnswer,
|
||||
captchaToken: req.body.captchaToken ? req.body.captchaToken.substring(0, 8) + '...' : undefined,
|
||||
captchaAnswer: req.body.captchaAnswer
|
||||
});
|
||||
if ( process.env.DEBUG ) {
|
||||
console.log('====== LOGIN CAPTCHA DIAGNOSTIC ======');
|
||||
console.log('LOGIN REQUEST RECEIVED with captcha data:', {
|
||||
hasCaptchaToken: !!req.body.captchaToken,
|
||||
hasCaptchaAnswer: !!req.body.captchaAnswer,
|
||||
captchaToken: req.body.captchaToken ? req.body.captchaToken.substring(0, 8) + '...' : undefined,
|
||||
captchaAnswer: req.body.captchaAnswer
|
||||
});
|
||||
}
|
||||
next();
|
||||
},
|
||||
requireCaptcha({ strictMode: true, eventType: 'login' }),
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -216,7 +216,7 @@ class Container {
|
||||
*/
|
||||
async emit (id, ...args) {
|
||||
if ( this.logger ) {
|
||||
this.logger.info(`services:event ${id}`, { args });
|
||||
this.logger.debug(`services:event ${id}`, { args });
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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) {
|
||||
|
||||
@@ -62,7 +62,7 @@ class OperationFrame {
|
||||
this.status_ = status;
|
||||
this._calc_effective_status();
|
||||
|
||||
this.log.info(
|
||||
this.log.debug(
|
||||
`FRAME STATUS ${status.label} ` +
|
||||
(status !== this.effective_status_
|
||||
? `(effective: ${this.effective_status_.label}) `
|
||||
@@ -275,7 +275,7 @@ class OperationTraceService {
|
||||
x
|
||||
});
|
||||
parent && parent.push_child(frame);
|
||||
this.log.info(`FRAME START ` + frame.describe());
|
||||
this.log.debug(`FRAME START ` + frame.describe());
|
||||
if ( ! parent ) {
|
||||
// NOTE: only uncomment in local testing for now;
|
||||
// this will cause a memory leak until frame
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
const { Endpoint } = require("../util/expressutil");
|
||||
const BaseService = require("./BaseService");
|
||||
|
||||
const aws = require('aws-sdk');
|
||||
const sns = new aws.SNS();
|
||||
const { LRUCache: LRU } = require('lru-cache');
|
||||
const crypto = require('crypto');
|
||||
const axios = require('axios');
|
||||
@@ -52,10 +50,6 @@ const TEST_CERT = ``;
|
||||
const TEST_MESSAGE = {};
|
||||
|
||||
class SNSService extends BaseService {
|
||||
static MODULES = {
|
||||
AWS: require('aws-sdk'),
|
||||
};
|
||||
|
||||
_construct () {
|
||||
this.cert_cache = new LRU({
|
||||
// Guide uses 5000 here but that seems excessive
|
||||
@@ -149,10 +143,6 @@ class SNSService extends BaseService {
|
||||
}).attach(app);
|
||||
}
|
||||
|
||||
_init () {
|
||||
this.sns = new this.modules.AWS.SNS();
|
||||
}
|
||||
|
||||
async verify_message_ (message, options = {}) {
|
||||
let cert;
|
||||
if ( options.test_mode ) {
|
||||
|
||||
@@ -438,7 +438,7 @@ class ACLService extends BaseService {
|
||||
await appdata_node.is_above(fsNode)
|
||||
)
|
||||
) {
|
||||
console.log('TRUE BECAUSE APPDATA');
|
||||
this.log.debug('TRUE BECAUSE APPDATA');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ class AuthService extends BaseService {
|
||||
throw APIError.create('forbidden');
|
||||
}
|
||||
|
||||
this.log.info(`generating user-app token for app ${app_uid} and user ${actor_type.user.uuid}`, {
|
||||
this.log.debug(`generating user-app token for app ${app_uid} and user ${actor_type.user.uuid}`, {
|
||||
app_uid,
|
||||
user_uid: actor_type.user.uuid,
|
||||
})
|
||||
@@ -251,7 +251,7 @@ class AuthService extends BaseService {
|
||||
* session's metadata.
|
||||
*/
|
||||
async create_session_ (user, meta = {}) {
|
||||
this.log.info(`CREATING SESSION`);
|
||||
this.log.debug(`CREATING SESSION`);
|
||||
|
||||
if ( meta.req ) {
|
||||
const req = meta.req;
|
||||
|
||||
@@ -179,7 +179,7 @@ class PermissionService extends BaseService {
|
||||
}
|
||||
async #scan(actor, permission_options, _reserved, state) {
|
||||
if ( ! state ) {
|
||||
this.log.info('scan', {
|
||||
this.log.debug('scan', {
|
||||
actor: actor.uid,
|
||||
permission_options,
|
||||
});
|
||||
|
||||
@@ -467,7 +467,7 @@ class DriverService extends BaseService {
|
||||
|
||||
effective_policy = effective_policy.policy;
|
||||
|
||||
this.log.info('Invoking Driver Call', {
|
||||
this.log.debug('Invoking Driver Call', {
|
||||
service_name,
|
||||
iface,
|
||||
method,
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = async (options = {}) => {
|
||||
const entries = [];
|
||||
|
||||
for ( const extensionsDir of extension_directories ) {
|
||||
console.log(`Reading extensions from ${extensionsDir}`);
|
||||
// console.log(`Reading extensions from ${extensionsDir}`);
|
||||
// Read and process extension entries from the extensions directory
|
||||
const readdir_entries = fs.readdirSync(extensionsDir, { withFileTypes: true })
|
||||
for ( const entry of readdir_entries ) {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user