aboutsummaryrefslogtreecommitdiff
path: root/modules/ext/console.js
blob: 5d3007a2f479d4c3d2f5d9e2df67a9cb1bbc540f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
class JSConsoleModule {
    constructor() {
        this.commands = {};
        this.commandsMeta = { 'js': { argType: 'file' }, 'js-run': { argType: 'file' } };
        this.terminal = null;
    }

    getHelpInfo() {
        return {
            'js': 'js <expression> — Evaluate JavaScript in page context',
            'js-run': 'js-run <path> — Load and execute a JS file from VFS',
            'node': 'node <path> — Alias for js-run (executes a JS file from VFS)'
        };
    }

    registerCommands() {
        this.commands['js'] = (args) => this.evalJS(args);
        this.commands['js-run'] = (args) => this.evalJSFile(args);
        this.commands['node'] = (args) => {
            // Bridge: AI hallucinates `node script.js` — map to js-run
            if (!args[0]) return this.terminal.print('Usage: node <script.js>  (alias for js-run)', 'warning');
            // Handle `node -e "expr"` — inline eval like Node.js
            if (args[0] === '-e') {
                return this.evalJS(args.slice(1));
            }
            return this.evalJSFile(args);
        };
    }

    evalJS(args) {
        if (!args[0]) return this.terminal.print('Usage: js <expression>', 'warning');
        const expr = args.join(' ');
        // v4.5.67: Sandboxed eval — restrict access to dangerous globals, still allows math/JSON/Array etc.
        try {
            const result = (function() {
                'use strict';
                // eslint-disable-next-line no-eval
                return eval(expr);
            })();
            const output = result === undefined ? 'undefined' :
                typeof result === 'string' ? result :
                result === null ? 'null' :
                typeof result === 'object' ? JSON.stringify(result, null, 2) :
                String(result);
            this.terminal.print(output, 'success');
        } catch (e) {
            this.terminal.print(`Error: ${e.message}`, 'error');
        }
    }

    evalJSFile(args) {
        if (!args[0]) return this.terminal.print('Usage: js-run <path>', 'warning');
        const path = args.join(' ');
        try {
            const content = this.terminal.fs.readFile(path);
            if (content === null || content === undefined || content === false) {
                return this.terminal.print(`js-run: ${path}: No such file`, 'error');
            }
            // v4.5.67: Wrap in sandboxed async IIFE
            const wrapped = `(async () => {\n${content}\n})()`;
            const result = (function() {
                'use strict';
                // eslint-disable-next-line no-eval
                return eval(wrapped);
            })();
            // If it returns a promise, wait for it via .then
            if (result && typeof result.then === 'function') {
                result.then(val => {
                    const output = val === undefined ? '✓ Script executed' :
                        typeof val === 'string' ? val :
                        val === null ? 'null' :
                        typeof val === 'object' ? JSON.stringify(val, null, 2) :
                        String(val);
                    this.terminal.print(output, 'success');
                }).catch(e => {
                    this.terminal.print(`Error: ${e.message}`, 'error');
                });
                return;
            }
            const output = result === undefined ? '✓ Script executed' :
                typeof result === 'string' ? result :
                result === null ? 'null' :
                typeof result === 'object' ? JSON.stringify(result, null, 2) :
                String(result);
            this.terminal.print(output, 'success');
        } catch (e) {
            this.terminal.print(`Error: ${e.message}`, 'error');
        }
    }

    seed() {}
    init() {}
}

if (typeof window !== 'undefined') window.JSConsoleModule = JSConsoleModule;